Python Object Persistence
Noted via #python on Twitter, a link to this discussion on creating a simple Movie and Actor database. Some suggestions for the original poster included using Shelve; myself I'd prefer to move up a rung or two and use a Python object database like ZODB or Durus, either of which can be used for anything from simple to complex solutions.
Attached is a dirt-simple module implementing a movie and actor database. Look ma, no SQL. The core of it is here, with the objects of interest subclassing Durus's Persistent class:
# Our "Models" -- it's just Python.
class Actor(Persistent):
def __init__(self, name):
self.name = name
def __str__(self):
return '<Actor: %s>' % self.name
class Movie(Persistent):
def __init__(self, title):
self.title = title
self.actors = PersistentSet()
def add_actor(self, actor):
self.actors.add(actor)
def __str__(self):
return '<Movie: %s>' % self.title
class Database(Persistent):
def __init__(self):
self.movies = PersistentDict()
self.actors = PersistentDict()
def add_movie(self, movie):
assert movie.title not in self.movies, 'Already exists!'
self.movies[movie.title] = movie
return movie
def get_movie(self, title):
return self.movies.get(title, None)
def add_actor(self, actor):
assert actor.name not in self.actors, 'Already exists!'
self.actors[actor.name] = actor
def get_actor(self, name):
return self.actors.get(name, None)
def appears_in(self, actor):
"""(actor : Actor) -> [Movie,]
Returns a sequence of Movies, if any, the Actor played a role in.
"""
return [m for m in self.movies.values() if actor in m.actors]
Objects which subclass the Persistent class become persistence-aware. Aside from having some special container classes for persistent dicts, lists, sets and BTrees for performance with large mappings, there isn't much of a mental burden to design objects that participate in a Durus or ZODB object database.
An example of retrieving some data from our film database:
if __name__ == '__main__':
from examples.durus.flicks.movies import load_it, get_db
from durus.file_storage import FileStorage
from durus.connection import Connection
connection = Connection(FileStorage("movies.durus"))
# load some sample data
if not connection.get_root().get('films', False):
load_it(connection)
db = get_db(connection)
hopkins = db.get_actor('Anthony Hopkins')
mel = db.get_actor('Mel Gibson')
# Which films has an actor appeared in?
for actor in (hopkins, mel):
print()
print('%s is in:' % actor.name)
print([str(m) for m in db.appears_in(actor)])
# What films are in our movie database and name known actors in each
print("\nComplete Movie Listing")
for m in db.movies.values():
print(m.title, ':', ','.join([str(a) for a in m.actors]))
Running this script the first time you'll note some log activity to stdout as the object instances are committed to the database. The listing output will look like this:
Anthony Hopkins is in: ['<Movie: Mutiny on the Bounty>'] Mel Gibson is in: ['<Movie: Braveheart>', '<Movie: Lethal Weapon II>', '<Movie: Mutiny on the Bounty>', '<Movie: Lethal Weapon III>', '<Movie: Lethal Weapon>'] Complete Movie Listing Braveheart : <Actor: Mel Gibson> Lethal Weapon II : <Actor: Mel Gibson>,<Actor: Danny Glover> Mutiny on the Bounty : <Actor: Mel Gibson>,<Actor: Anthony Hopkins> Lethal Weapon III : <Actor: Mel Gibson>,<Actor: Danny Glover> Lethal Weapon : <Actor: Mel Gibson>,<Actor: Danny Glover>
(No commentary on my taste for films, please)
In addition to the FileStorage class, Durus offers a client-server solution ClientStorage suitable for use by multi-process applications such as web apps. ZODB provides the same through its ZEO feature.