mysql user conference 2009: python and mysql

73
Python and MySQL Ted Leung Sun Microsystems

Upload: ted-leung

Post on 27-Jan-2015

123 views

Category:

Technology


4 download

DESCRIPTION

 

TRANSCRIPT

Page 1: MySQL User Conference 2009: Python and MySQL

Python and MySQLTed LeungSun Microsystems

Page 2: MySQL User Conference 2009: Python and MySQL
Page 3: MySQL User Conference 2009: Python and MySQL
Page 4: MySQL User Conference 2009: Python and MySQL

Python-DB In the beginning there was the DB-API PEP-249

Page 5: MySQL User Conference 2009: Python and MySQL

MySQLdb MySQLdb written in Python _mysql written in C and is MySQL specific

Page 6: MySQL User Conference 2009: Python and MySQL

TextTextText

import MySQLdb

conn = MySQLdb.connect( host = 'localhost', user = 'twl', passwd='pass', db='flickrmgr')

Text

Page 7: MySQL User Conference 2009: Python and MySQL

c = conn.cursor()c.execute("""SELECT * from curator_photo""")

# read one rowpprint(c.fetchone())

# read all the rowspprint(c.fetchall())

((3L, 3422686825L, "OPG Toymaker's Doll 2009", 2L, 'http://farm4.static.flickr.com/3377/3422686825_fd57ea30e7.jpg', datetime.datetime(2009, 4, 8, 1, 21, 44)), (4L, 3422685683L, "OPG Toymaker's Doll 2009", 2L, 'http://farm4.static.flickr.com/3608/3422685683_4d16829c19.jpg', datetime.datetime(2009, 4, 8, 1, 20, 54)),

Page 8: MySQL User Conference 2009: Python and MySQL

dc = conn.cursor(MySQLdb.cursors.DictCursor)dc.execute("""SELECT * from curator_photo""")

pprint(dc.fetchmany(5))

({'flickr_id': 2147483647L, 'id': 2L, 'pub_date': datetime.datetime(2009, 4, 8, 1, 21, 44), 'title': "OPG Toymaker's Doll 2009", 'url': 'http://farm4.static.flickr.com/3377/3422686825_fd57ea30e7.jpg', 'user_id': 2L}, {'flickr_id': 3422686825L, 'id': 3L, 'pub_date': datetime.datetime(2009, 4, 8, 1, 21, 44), 'title': "OPG Toymaker's Doll 2009", 'url': 'http://farm4.static.flickr.com/3377/3422686825_fd57ea30e7.jpg', 'user_id': 2L})

Page 9: MySQL User Conference 2009: Python and MySQL

c.execute( """SELECT * from curator_photo WHERE id < %s""", (50,))

pprint(c.fetchall())

Page 10: MySQL User Conference 2009: Python and MySQL

c.execute( """INSERT INTO curator_user (flickr_id, name) VALUES (%s, %s)""", ("000", "No User"))

conn.commit()

Page 11: MySQL User Conference 2009: Python and MySQL

Django Leading web application framework Lots of people coming to Python via Django today Model-View-Controller Command line management scripts It’s own Rails-like ORM Based on the Active Record pattern

Page 12: MySQL User Conference 2009: Python and MySQL

Active Record Pattern Database table is wrapped in a class Each class instance is a row in the table Relationships expressd as foreign key constraints

Page 13: MySQL User Conference 2009: Python and MySQL

DATABASE_ENGINE = 'mysql' DATABASE_NAME = 'flickrmgr'DATABASE_USER = 'twl' DATABASE_PASSWORD = 'pass'

Page 14: MySQL User Conference 2009: Python and MySQL

from django.db import models

class User(models.Model): flickr_id = models.TextField() name = models.TextField()

def __unicode__(self): return self.name

class Group(models.Model): flickr_id = models.TextField() name = models.TextField() throttle_count = models.IntegerField() throttle_mode = models.TextField() throttle_remaining = models.IntegerField()

def __unicode__(self): return self.name

Page 15: MySQL User Conference 2009: Python and MySQL

class Photo(models.Model): flickr_id = models.IntegerField() title = models.TextField() user = models.ForeignKey(User) groups = models.ManyToManyField(Group) url = models.URLField() pub_date = models.DateTimeField()

def __unicode__(self): return self.title

Page 16: MySQL User Conference 2009: Python and MySQL

from curator.models import Photofrom datetime import datetime

Photo.objects.all()Photo.objects.filter(title__contains='PyCon')

Photo.objects.filter(title__contains='PyCon').exclude( pub_date__lte=datetime(2009,4,1))

Photo.objects.filter(title__contains='PyCon').exclude( pub_date__lte=datetime(2009,4,1))[2:4]

Page 17: MySQL User Conference 2009: Python and MySQL

from django.contrib import admin

class PhotoAdmin(admin.ModelAdmin): pass

class GroupAdmin(admin.ModelAdmin): pass

class UserAdmin(admin.ModelAdmin): pass

admin.site.register(Group, GroupAdmin)admin.site.register(Photo, PhotoAdmin)admin.site.register(User, UserAdmin)

Page 18: MySQL User Conference 2009: Python and MySQL

def load_from_flickr(request): api = API()

# 51035696189@N01 twl, created = User.objects.get_or_create( flickr_id = '51035696189@N01', name = 'Ted Leung' ) if created: twl.save()

photos = api.list_user_info( 'http://www.flickr.com/photos/twleung')

Page 19: MySQL User Conference 2009: Python and MySQL

for photo in photos: flickr_id, title, pub_date, url, pools = photo new_photo = Photo() new_photo.flickr_id = flickr_id new_photo.title = title new_photo.user = twl new_photo.pub_date = datetime.fromtimestamp(int(pub_date)) new_photo.url = url new_photo.save()

Page 20: MySQL User Conference 2009: Python and MySQL

# do pools for pool in pools: new_pool, created=Group.objects.get_or_create( flickr_id = pool[0], name = pool[1], throttle_count = pool[2], throttle_mode = pool[3], throttle_remaining = pool[4] ) new_photo.groups.add(new_pool) if created: new_pool.save()

Page 21: MySQL User Conference 2009: Python and MySQL

output = '''<html> <head> <title>Bulk loading from Flickr</title> </head> <body> </body></html>''' return HttpResponse(output)

Page 22: MySQL User Conference 2009: Python and MySQL

from django.conf.urls.defaults import *

from curator.models import User, Group, Photofrom curator.views import load_from_flickr

photo_info_dict = { 'queryset': Photo.objects.all(), 'date_field': 'pub_date',}

urlpatterns = patterns( '', (r'^$', 'django.views.generic.date_based.archive_index', photo_info_dict), (r'^load/$', load_from_flickr),)

Page 23: MySQL User Conference 2009: Python and MySQL

Transactions Commit on save or delete Commit at HTTP request/response boundaries commit_manually decorator

Page 24: MySQL User Conference 2009: Python and MySQL

Connection Pooling Important as systems scale up No framework wide solution yet

Page 25: MySQL User Conference 2009: Python and MySQL

Migration South Integrates with manage.py Can automatically migrate models

Page 26: MySQL User Conference 2009: Python and MySQL

../bin/django startmigration curator --initial

Creating migrations directory at '/Users/twl/work/mysql-2009/django/flickrmgr/curator/migrations'...

Creating __init__.py in '/Users/twl/work/mysql-2009/django/flickrmgr/curator/migrations'... + Added model 'curator.Photo' + Added model 'curator.Group' + Added model 'curator.User' + Added field 'curator.Photo.groups'Created 0001_initial.py.

Page 27: MySQL User Conference 2009: Python and MySQL

class Photo(models.Model): flickr_id = models.IntegerField() title = models.TextField() user = models.ForeignKey(User) groups = models.ManyToManyField(Group) url = models.URLField() pub_date = models.DateTimeField() visible = models.BooleanField()

Page 28: MySQL User Conference 2009: Python and MySQL

../bin/django startmigration curator add_visible --auto + Added field 'curator.photo.visible'Created 0002_add_visible.py.

Page 29: MySQL User Conference 2009: Python and MySQL

from south.db import dbfrom django.db import modelsfrom curator.models import *

class Migration: def forwards(self, orm): # Adding field 'Photo.visible' db.add_column('curator_photo', 'visible', models.BooleanField()) def backwards(self, orm): # Deleting field 'Photo.visible' db.delete_column('curator_photo', 'visible')

Page 30: MySQL User Conference 2009: Python and MySQL

SQLObject ORM using Active Record pattern Tight coupling to Python classes Used in TurboGears 1

Page 31: MySQL User Conference 2009: Python and MySQL

connection_string='mysql://twl:pass@localhost/sqlobject'

connection=connectionForURI(connection_string)

sqlhub.processConnection = connection

try: User.createTable(ifNotExists=True) Photo.createTable(ifNotExists=True) FlickrGroup.createTable(ifNotExists=True)except dberrors.OperationalError, oe: print oe

Page 32: MySQL User Conference 2009: Python and MySQL

class User(SQLObject): flickr_id = StringCol() name = StringCol()

class Photo(SQLObject): flickr_id = StringCol() title = StringCol() user = ForeignKey('User') groups = RelatedJoin('FlickrGroup') url = StringCol() pub_date = DateTimeCol()

class FlickrGroup(SQLObject): flickr_id = StringCol() name = StringCol() throttle_count = IntCol() throttle_mode = StringCol() throttle_remaining = IntCol() photos = RelatedJoin('Photo')

Page 33: MySQL User Conference 2009: Python and MySQL

pycon_photos = list(Photo.select(Photo.q.title.contains('PyCon')))

pprint(pycon_photos)

pycon_apr_photos = list(Photo.select(AND(Photo.q.title.contains('PyCon'), Photo.q.pub_date > datetime(2009,4,1) )))

pprint(pycon_apr_photos)

Page 34: MySQL User Conference 2009: Python and MySQL

api = API()

twl = User( flickr_id = '51035696189@N01', name = 'Ted Leung')

photos = api.list_user_info( 'http://www.flickr.com/photos/twleung')

Page 35: MySQL User Conference 2009: Python and MySQL

for photo in photos: print photo flickr_id, title, pub_date, url, pools = photo new_photo = Photo( flickr_id = flickr_id, title = title, user = twl, pub_date = datetime.fromtimestamp(int(pub_date)), url = url )

Page 36: MySQL User Conference 2009: Python and MySQL

# do pools for pool in pools: new_pool = list(FlickrGroup.select( FlickrGroup.q.flickr_id == pool[0])) if len(new_pool) > 0: new_pool = new_pool[0] else: new_pool = None if not new_pool: new_pool = FlickrGroup( flickr_id = pool[0], name = pool[1], throttle_count = pool[2], throttle_mode = pool[3], throttle_remaining = pool[4] )

new_photo.addFlickrGroup(new_pool)

Page 37: MySQL User Conference 2009: Python and MySQL

Transactionstxn = connection.transaction()

p = Photo.get(1, txn)p.title = ‘updated photo’

txn.commit()

Page 38: MySQL User Conference 2009: Python and MySQL

Connection Pooling Connection pooling is built in and on by default

Page 39: MySQL User Conference 2009: Python and MySQL

sqlmetaclass Photo(SQLObject): class sqlmeta: lazyUpdate = True cacheValues = False

flickr_id = StringCol() title = StringCol() user = ForeignKey('User') groups = RelatedJoin('FlickrGroup') url = StringCol() pub_date = DateTimeCol()

Page 40: MySQL User Conference 2009: Python and MySQL

Eventsfrom sqlobject.events import listenfrom sqlobject.events RowUpdateSignal, RowCreatedSignal

def update_listener(instance, kwargs): kwargs['pub_date'] = datetime.datetime.now()

def created_listener(kwargs, post_funcs): print “created photo %s” % (kwargs[‘title’])

listen(update_listener, Photo, RowUpdateSignal)listen(created_listener, Photo, RowCreatedSignal)

Page 41: MySQL User Conference 2009: Python and MySQL

SQLAlchemy Python’s answer to Hibernate Low level SQL manipulations High Level ORM Used in TurboGears 2

Page 42: MySQL User Conference 2009: Python and MySQL

Low Level SQL

Page 43: MySQL User Conference 2009: Python and MySQL

from sqlalchemy import create_enginefrom sqlalchemy import Table, Column, Integer, String, Text, DateTime, MetaData, ForeignKeyfrom sqlalchemy import select

engine = create_engine( 'mysql://twl:pass@localhost/sqlobject',echo=True) metadata = MetaData()

Page 44: MySQL User Conference 2009: Python and MySQL

users_table = Table('user', metadata, Column('id', Integer, primary_key=True), Column('flickr_id', Text), Column('name', Text) )

groups_table = Table('group', metadata, Column('id', Integer, primary_key=True), Column('flickr_id', Text), Column('name', Text), Column('throttle_count', Integer), Column('throttle_mode', Text), Column('throttle_remaining', Integer) ) photos_table = Table('photo', metadata, Column('id', Integer, primary_key=True), Column('flickr_id', Text), Column('title', Text), Column('user_id', Integer, ForeignKey(‘user.id’)), Column('url', Text), Column('pub_date', DateTime) )

Page 45: MySQL User Conference 2009: Python and MySQL

metadata.create_all(engine)

conn = engine.connect()

s = select([users_table])result = conn.execute(s)

for row in result: print row

s = select([photos_table], photos_table.c.title.contains('PyCon'))

for row in conn.execute(s): print row

Page 46: MySQL User Conference 2009: Python and MySQL

ins = users.insert().values(name='Thomas Hawk', flickr_id='000')

result = conn.execute(ins)

update = users.update().where( users.c.name=='Thomas Hawk' ).values( flickr_id='51035555243@N01'))

result = conn.execute(update)

Page 47: MySQL User Conference 2009: Python and MySQL

Transactions Regular manual control Autocommit - per session 2PC

Page 48: MySQL User Conference 2009: Python and MySQL

Connection Pooling Built-in framework Done at engine configuration time

Page 49: MySQL User Conference 2009: Python and MySQL

Other Low Level SQL features Unions and other Set operations Scalar Selects Correlated Subqueries Ordering, Grouping, Offsetting Correlated Updates

Page 50: MySQL User Conference 2009: Python and MySQL

ORM Mapping Declarative

Page 51: MySQL User Conference 2009: Python and MySQL

class User(object): def __repr__(self): return "<User('%s','%s','%s')>" % ( self.id, self.flickr_id, self.name)

class Group(object): def __repr__(self): return "<Group('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.name, self.throttle_remaining )

class Photo(object): def __repr__(self): return "<Photo('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.title, self.url )

Page 52: MySQL User Conference 2009: Python and MySQL

metadata = MetaData()

flickr_group_photo = Table( 'flickr_group_photo', metadata, Column('flickr_group_id', Integer, ForeignKey('flickr_group.id')), Column('photo_id', Integer, ForeignKey('photo.id')))

Page 53: MySQL User Conference 2009: Python and MySQL

from sqlalchemy.orm import mapper, sessionmaker

engine = create_engine( 'mysql://twl:pass@localhost/sqlobject')

Page 54: MySQL User Conference 2009: Python and MySQL

Session = sessionmaker(bind = engine)session = Session()

mapper(User, users_table, properties = { 'photos' : relation(Photo, backref='user') })

mapper(Group, groups_table, properties = { 'photos' : relation(Photo, secondary=flickr_group_photo, backref='groups') })

mapper(Photo, photos_table)

for u in session.query(User): print u

for g in session.query(Group): print g

for p in session.query(Photo): print p

Page 55: MySQL User Conference 2009: Python and MySQL

Declarative ORM

Page 56: MySQL User Conference 2009: Python and MySQL

from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmakerfrom sqlalchemy import Column, ForeignKeyfrom sqlalchemy import Integer, String, Text, DateTime

Base = declarative_base()

class User(Base): __tablename__ = 'user'

id = Column(Integer, primary_key=True) flickr_id = Column(Text) name = Column(Text)

def __repr__(self): return "<User('%s','%s','%s')>" % ( self.id, self.flickr_id, self.name)

Page 57: MySQL User Conference 2009: Python and MySQL

class Group(Base): __tablename__ = 'flickr_group' id = Column(Integer, primary_key=True) flickr_id = Column(Text) name = Column(Text) throttle_count = Column(Integer) throttle_mode = Column(Text) throttle_remaining = Column(Integer)

def __repr__(self): return "<Group('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.name, self.throttle_remaining )

Page 58: MySQL User Conference 2009: Python and MySQL

class Photo(Base): __tablename__ = 'photo' id = Column(Integer, primary_key=True) flickr_id = Column(Text) title = Column(Text) user_id = Column(Integer,ForeignKey('user.id')) user = relation(User, backref=backref('photos', order_by=id)) groups = relation('Group', secondary=flickr_group_photo, backref=backref('photos')) url = Column(Text) pub_date = Column(DateTime)

def __repr__(self): return "<Photo('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.title, self.url )

Page 59: MySQL User Conference 2009: Python and MySQL

Session = sessionmaker(bind = engine)session = Session()

for u in session.query(User): print u

for g in session.query(Group): print g

for p in session.query(Photo): print p

Page 60: MySQL User Conference 2009: Python and MySQL

And more Advanced mapping Multiple table mapping

Page 61: MySQL User Conference 2009: Python and MySQL

Elixir a simpler way of doing declarative ORM for

SQLAlchemy

Page 62: MySQL User Conference 2009: Python and MySQL

from elixir import *

class User(Entity): using_options(tablename = 'user')

flickr_id = Field(Text) name = Field(Text) photos = OneToMany('Photo')

def __repr__(self): return "<User('%s','%s','%s')>" % ( self.id, self.flickr_id, self.name)

Page 63: MySQL User Conference 2009: Python and MySQL

class Group(Entity): using_options(tablename = 'flickr_group') flickr_id = Field(Text) name = Field(Text) throttle_count = Field(Integer) throttle_mode = Field(Text) throttle_remaining = Field(Integer)

def __repr__(self): return "<Group('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.name, self.throttle_remaining )

Page 64: MySQL User Conference 2009: Python and MySQL

class Photo(Entity): using_options(tablename = 'photo')

flickr_id = Field(Text) title = Field(Text) url = Column(Text) pub_date = Column(DateTime) groups = ManyToMany('Group') user = ManyToOne('User')

def __repr__(self): return "<Photo('%s','%s','%s','%s')>" % ( self.id, self.flickr_id, self.title, self.url )

Page 65: MySQL User Conference 2009: Python and MySQL

engine = create_engine( 'mysql://twl:pass@localhost/sqlobject', echo=True) Session = sessionmaker(bind = engine)session = Session()

metadata = MetaData()

metadata.bind='mysql://twl:pass@localhost/sqlobject'metadata.bind.echo = True

setup_all()

for u in session.query(User): print u

for g in session.query(Group): print g

for p in session.query(Photo): print print

Page 66: MySQL User Conference 2009: Python and MySQL

SqlSoupfrom sqlalchemy.ext.sqlsoup import SqlSoup

soup = SqlSoup('mysql://twl:pass@localhost/sqlobject')

soup.photo.all()

Page 67: MySQL User Conference 2009: Python and MySQL

[MappedPhoto(id=71L,flickr_id='3286255071',title='',user_id=1L,url='http://farm4.static.flickr.com/3249/3286255071_ff168f220b.jpg',pub_date=datetime.datetime(2009, 2, 16, 20, 47, 56)), MappedPhoto(id=72L,flickr_id='3287070244',title='',user_id=1L,url='http://farm4.static.flickr.com/3298/3287070244_87a2a1b3ed.jpg',pub_date=datetime.datetime(2009, 2, 16, 20, 47, 22)), MappedPhoto(id=46L,flickr_id='3395647634',title='Andy Dustman',user_id=1L,url='http://farm4.static.flickr.com/3554/3395647634_cc0c9f5a0a.jpg',pub_date=datetime.datetime(2009, 3, 29, 9, 7, 34)), MappedPhoto(id=73L,flickr_id='3277628077',title='Bainbridge Island Chinese New Year 2009',user_id=1L,url='http://farm4.static.flickr.com/3334/3277628077_78025002f5.jpg',pub_date=datetime.datetime(2009, 2, 13, 23, 31, 52)),

soup.photo.order_by(soup.photo.title).all()

Page 68: MySQL User Conference 2009: Python and MySQL

Migrations sqlalchemy-migrate Version control a database A repository of upgrade scripts

Page 69: MySQL User Conference 2009: Python and MySQL

from sqlalchemy import *from migrate import *

metadata = MetaData(migrate_engine)

photos_table = Table('photo', metadata, Column('id', Integer, primary_key=True), Column('flickr_id', Text), Column('title', Text), Column('user_id', Integer), Column('url', Text), Column('pub_date', DateTime), )

visible_col = Column('visible', Integer)

def upgrade(): visible_col.create(photos_table) assert visible_col is photos_table.c.visible

def downgrade(): visible_col.drop()

Page 70: MySQL User Conference 2009: Python and MySQL

from sqlalchemy import *from migrate import *

metadata = MetaData(migrate_engine)

photos_table = Table('photo', metadata, Column('id', Integer, primary_key=True), Column('flickr_id', Text), Column('title', Text), Column('user_id', Integer), Column('url', Text), Column('pub_date', DateTime), Column('visible', Integer) )

col = photos_table.c.visible

def upgrade(): col.alter(type=Boolean, nullable=False)

def downgrade(): col.alter(type=Integer, nullable=True)

Page 71: MySQL User Conference 2009: Python and MySQL

Migration operations Create/Drop a table Create/Drop/Alter a column Create/Drop index Create/Drop primary/foreign key constraints

Page 72: MySQL User Conference 2009: Python and MySQL

Summary Django for Django SQLAlchemy for the rest