The following code models a simple system that tracks the transfer of
construction tools between jobs. Equip (equipment) is transferred
between Jobs via Shipments.
Towards the end I attempt to map a class to a select statement in
order to make reporting simple. Instead of dealing with sql to do the
reporting, I wanted to map an object to a summarizing sql statement,
and create a sort of object model that covers most of the summarizing
I will need to do.
I can't figure out how to map an object to a select statement and
include a relation in the object. The code below should run in python
2.6:
from sqlalchemy import *
from sqlalchemy.orm import mapper, sessionmaker, relation
from sqlalchemy.sql import *
from datetime import date
# SA objects
db = create_engine('sqlite://', echo=True)
meta = MetaData()
session = sessionmaker(bind=db)()
# Table schema
job = Table('job', meta,
Column('id', Integer, primary_key=True),
Column('number', Integer))
equip = Table('equip', meta,
Column('id', Integer, primary_key=True),
Column('name', Unicode(255)))
equip_shipment = Table('equip_shipment', meta,
Column('id', Integer, primary_key=True),
Column('shipDate', Date),
Column('fromJobId', Integer, ForeignKey('job.id')),
Column('toJobId', Integer, ForeignKey('job.id')),
Column('isBroken', Boolean))
equip_shipment_item = Table('equip_shipment_item', meta,
Column('id', Integer, primary_key=True),
Column('shipmentId', Integer, ForeignKey
('equip_shipment.id')),
Column('equipId', Integer, ForeignKey('equip.id')),
Column('qty', Integer))
meta.create_all(db)
# Objects
class KeywordInitMixin(object):
'''Fills object's attributes with whatever keyword args were given
to init.
As an example, allows me to simply inherit from this class like
this:
class Test(KeywordInitMixin):
pass
...And then create objects like this:
t = Test(foo=1, bar='spam')
assert t.foo == 1
assert t.bar == 'spam'
'''
def __init__(self, **kwargs):
for attr in self.ATTRS:
if attr in kwargs:
setattr(self, attr, kwargs[attr])
else:
setattr(self, attr, None)
# Set any properties
for attr, val in kwargs.items():
# See if class has a property by this name
if (hasattr(self.__class__, attr) and
getattr(self.__class__, attr).__class__ is property):
setattr(self, attr, val)
def __repr__(self):
args = ['%s=%s' % (arg, val) for arg, val in
self.__dict__.items() if
arg in self.ATTRS and val]
args = ', '.join(args)
name = self.__class__.__name__
result = '%s(%s)' % (name, args)
return result
class Job(KeywordInitMixin):
ATTRS = ['number']
class Equip(KeywordInitMixin):
ATTRS = ['name']
class Shipment(KeywordInitMixin):
ATTRS = ['shipDate', 'fromJob', 'toJob', 'isBroken']
class ShipmentItem(KeywordInitMixin):
ATTRS = ['shipment', 'equip', 'qty']
# Map schema to objects
mapper(Job, job)
mapper(Equip, equip)
mapper(Shipment, equip_shipment,
properties={
'fromJob': relation(Job,
primaryjoin=equip_shipment.c.fromJobId==job.c.id),
'toJob': relation(Job,
primaryjoin=equip_shipment.c.toJobId==job.c.id),
}
)
mapper(ShipmentItem, equip_shipment_item,
properties={
'shipment': relation(Shipment, backref='items'),
'equip': relation(Equip)
}
)
# -------------------------------------
# Create some test data
# -------------------------------------
# Jobs
warehouse1 = Job(number=10001)
job1 = Job(number=11111)
job2 = Job(number=22222)
# Equipment
bClamps = Equip(name=u'Bridge Clamps')
cLocks = Equip(name=u'420 Channel Lock')
smallLock = Equip(name=u'Small 3210 Lock')
toolChest = Equip(name=u'Tool Chest')
# Add to orm
session.add_all([warehouse1, job1, job2, bClamps, cLocks])
# Ship tools to job 1
ship1 = Shipment(fromJob=warehouse1, toJob=job1, shipDate=date.today
())
ship1.items.append(ShipmentItem(qty=5, equip=bClamps))
# Transfer tools from job 1 to job 2
ship2 = Shipment(fromJob=job1, toJob=job2, shipDate=date.today())
ship2.items.append(ShipmentItem(qty=2, equip=bClamps))
# Job 1 returns some tools to the warehouse
ship3 = Shipment(fromJob=job1, toJob=warehouse1, shipDate=date.today
())
ship3.loadedBy = ship3.deliveredBy = 'jane doe'
ship3.items.append(ShipmentItem(qty=2, equip=smallLock))
# Add to orm
session.add_all([ship1, ship2, ship3])
# Job 1 breaks some tools
broken = Shipment(fromJob=job1, isBroken=True, shipDate=date.today())
broken.items.append(ShipmentItem(qty=1, equip=smallLock))
# Break more of same equip, but in different line item to test
aggregation
broken.items.append(ShipmentItem(qty=4, equip=smallLock))
# Job 2 breaks stuff too
broken2 = Shipment(fromJob=job2, isBroken=True, shipDate=date.today())
broken2.items.append(ShipmentItem(qty=1, equip=bClamps))
# Save
session.add_all([broken, broken2])
session.flush()
session.commit()
# -------------------------------------
# Create read-only reporting object that is based on a query that
# summarizes the equipment that has been broken by each job.
# -------------------------------------
class BrokenItem(object):
'''One row for each piece of equipment that a job has broken, and
the qty that has been broken.'''
pass
SELECT = [
equip_shipment.c.fromJobId,
equip.c.name.label('equip'),
func.sum(equip_shipment_item.c.qty).label('broken'),
]
FROM = [equip_shipment_item.join(equip_shipment).join(equip)]
sql = select(SELECT, from_obj=FROM)
sql = sql.where(equip_shipment.c.isBroken==True)
sql = sql.group_by(equip.c.name)
sql = sql.alias()
# Map to object
props = {
'job': relation(Job)
}
# I chose a primary key here because SA needs one
mapper(BrokenItem, sql, properties=props, primary_key=
[sql.c.fromJobId])
# ----------------------------------
# Test report object
# ----------------------------------
# Find what job 22222 has broke
# !! This causes an error because the table is joined in a way that
there
# is a row returned for each job !!
broke = session.query(BrokenItem).filter(Job.number==22222).one()
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---