So far, our database program consists of class instances stored in a shelve file, as coded in the preceding section. It’s sufficient as a storage medium, but it requires us to run scripts from the command line or type code interactively in order to view or process its content. Improving on this is straightforward: simply code more general programs that interact with users, either from a console window or from a full-blown graphical interface.
Let’s start with something simple. The most basic kind of interface we can code would allow users to type keys and values in a console window in order to process the database (instead of writing Python program code). Example 2-21, for instance, implements a simple interactive loop that allows a user to query multiple record objects in the shelve by key.
Example 2-21. PP3EPreviewpeopleinteract_query.py
# interactive queries import shelve fieldnames = ('name', 'age', 'job', 'pay') maxfield = max(len(f) for f in fieldnames) db = shelve.open('class-shelve') while True: key = raw_input(' Key? => ') # key or empty line, exc at eof if not key: break try: record = db[key] # fetch by key, show in console except: print 'No such key "%s"!' % key else: for field in fieldnames: print field.ljust(maxfield), '=>', getattr(record, field)
This script uses getattr
to
fetch an object’s attribute when given its name string, and the
ljust
left-justify method of
strings to align outputs (maxfield
, derived from a comprehension
expression, is the length of the longest field name). When run, this
script goes into a loop, inputting keys from the interactive user
(technically, from the standard input stream, which is usually a
console window) and displaying the fetched records field by field.
An empty line ends the session:
Key? =>sue
name => Sue Jones age => 45 job => music pay => 40000 Key? =>nobody
No such key "nobody"! Key? =>
Example 2-22 goes further and allows interactive updates. For an input key, it inputs values for each field and either updates an existing record or creates a new object and stores it under the key.
Example 2-22. PP3EPreviewpeopleinteract_update.py
# interactive updates import shelve from person import Person fieldnames = ('name', 'age', 'job', 'pay') db = shelve.open('class-shelve') while True: key = raw_input(' Key? => ') if not key: break if key in db.keys( ): record = db[key] # update existing record else: # or make/store new rec record = Person(name='?', age='?') # eval: quote strings for field in fieldnames: currval = getattr(record, field) newtext = raw_input(' [%s]=%s new?=>' % (field, currval)) if newtext: setattr(record, field, eval(newtext)) db[key] = record db.close( )
Notice the use of eval
in
this script to convert inputs (as usual, that allows any Python
object type, but it means you must quote string inputs explicitly)
and the use of setattr
call to
assign an attribute given its name string. When run, this script
allows any number of records to be added and changed; to keep the
current value of a record’s field, press the Enter key when prompted
for a new value:
Key? =>tom
[name]=Tom Doe new?=> [age]=55 new?=>56
[job]=mgr new?=> [pay]=65000.0 new?=>90000
Key? =>nobody
[name]=? new?=>'John Doh'
[age]=? new?=>55
[job]=None new?=> [pay]=0 new?=>None
Key? =>
This script is still fairly simplistic (e.g., errors aren’t handled), but using it is much easier than manually opening and modifying the shelve at the Python interactive prompt, especially for nonprogrammers. Run the query script to check your work after an update (we could combine query and update into a single script if this becomes too cumbersome, albeit at some cost in code and user-experience complexity):
Key? =>tom
name => Tom Doe age => 56 job => mgr pay => 90000 Key? =>nobody
name => John Doh age => 55 job => None pay => None Key? =>
18.218.97.75