Howto generlize database descriptions?

Sam Penrose sam at localhost.localdomain
Mon Sep 17 20:12:33 EDT 2001


On 17 Sep 2001, Danny Kohn wrote:

>> Guess that tuples or tuples in tuples may be a way to describe it but
then,
>> how efficient with the program execution be with all these references
being
>> used every time. I'm looking for something like define/struc in C
where I can
>> build my database from and then use the exact same data to access it.

Ignacio Vazquez-Abrams recommended:
> Look into using dictionaries.

What he said. Personally I keep table creation statements in files which
I pipe into MySQL, but in general dictionaries are the natural way to
handle data in Python that has is going to be passed to/from an SQL
INSERT, UPDATE, or SELECT statement.

Here is a hastily edited excerpt from our database abstraction layer. I
did the editing in my email client and so the spacing may not be right;
there may be NameErrors, etc. USE AT OWN RISK. If there is sufficient
interest I will try to post a complete and cleaned up version. The
printQuery method is a rather miserable hack; I'm sure there are other
infelicities.

import MySQLdb, types
ProgrammingError = MySQLdb.ProgrammingError
MySQLdb.type_conv[MySQLdb.FIELD_TYPE.LONG] = int 
# so that MySQL int(11) values 
# will be returned as ints, not longs. Would cause a ValueError 
# if you actually had a 9+ digit integer stored in there.

import masterSharedPath, DateTime 
# masterSharedPath puts DateTime on path; this is here to remind y'all
# that MySQLdb's use of mxDateTime and some design choices in both
# MySQLdb and mxDateTime can give you grief when handling SQL date/time
# objects

class MySQLConnection(SQLConnection):
    def __init__(self, database, user, passwd, host='localhost'):
        """Usage: db = MySQLConnection('database', 'user',
'passwd')."""
        self.connection = MySQLdb.connect(
               host=host, user=user, passwd=passwd, db=database)
        self.cursor = self.connection.cursor()
        self.dictCursor = self.connection.cursor(MySQLdb.DictCursor)

class SQLConnection:
    """An abstract class to hold methods that construct query strings;
base classes must create the self.connection and self.cursor objects
in their __init__s."""
    connection = None
    cursor = None
    dictCursor = None
    debug = 0 # see printQuery

    def _select(self, table, fields, whereClause, sort, limit,
cursor=None):
        """Return up to <limit> results of
        SELECT fields FROM table WHERE whereClause sortBy;
        (where 'fields' is a string or list of strings, whereClause and
sortBy are both optional but sort has to begin with 'order by'
or 'group by' or another valid SQL specifier) as a tuple
containing either tuples or dictionaries, depending on which
cursor we are using.

        Limit can be an integer (inside a string or otherwise), or a
string such as '10, 5' which will return records 11, 12, 13, 14,
and 15 (like the Python slice [10:10+5]."""
        if not cursor: 
            cursor = self.cursor
        if whereClause: 
            whereClause = 'WHERE ' + whereClause
        if limit: 
            limit = 'LIMIT %s' % limit
        fields = stringFields(fields)
        query = 'SELECT %s FROM %s %s %s %s' % (
             fields, table, whereClause, sort, limit)
        if self.debug: 
            print query
        else:
            cursor.execute(query)
            return cursor.fetchall()
    
    def select(self, table, fields, whereClause='', sort='', limit=''):
        """Return a tuple of Python dictionaries, one dictionary per row
        whose keys are the field names and values the corresponding
values.

        MySQLdb will strip off the table names if you pass them in
unless they are ambiguous,
        in which case it will attach *some* of the table names for
*some* of the ambiguous fields. This gets ugly. Solution: 
        do not use this query if you are joining tables
        and selecting same-named columns from > 1 table."""
        return self._select(table, fields, whereClause, sort, limit,
self.dictCursor)
        
    def selectTuples(self, table, fields, whereClause='', sort='',
limit=''):
        """Return a tuple of tuples."""
        return self._select(table, fields, whereClause, sort, limit)

    def singleSelect(self, table, field, whereClause=''):
        """Return the value of a single field in a single row, with no
tuple- or dictionary-wrapping."""
        results = self._select(table, field, whereClause, '', '')
        if len(results) != 1:
            message = 'singleSelect returned %s results' % len(results)
            raise ProgrammingError, message
        return results[0][0]
    
    def selectList(self, table, field, whereClause='', sort='',
limit=''):
        """Return a list of the values of a single field according to a
whereClause; raise error if asked for more than one field.

        Commonly you want to get, say, all the primary keys. select and
        selectTuples will package them into single-item dictionaries or
tuples, respectively, and return those in a list. This method
will return a list containing the bare values."""
        userField = field # used if error
        field = stringFields(field)
        if ',' in field:
            message = '''selectList takes only one field; you passed it
            "%s".''' % userField
            raise ProgrammingError, message
        
        tupleOfTuples = self._select(
                table, field, whereClause, sort, limit)
        return [t[0] for t in tupleOfTuples]

    def printQuery(self, selfDotMethod, args):
        """Print to screen the string you have been sending to the
database.

        Example:
            db = MySQLConnection('database', 'user',
'passwd')
            db.select('table', 'fields', 'whereClause')
            ...incomprehensible error raised; what's going on here?...
            db.printQuery(db.select, ('table', 'fields',
'whereClause'))
        """
        self.debug = 1
        apply(selfDotMethod, args)
        self.debug = 0


def stringFields(fields):
    '''Take in either a list or a string of field names and return a
string.'''
    if type(fields) is types.StringType: 
        return fields
    return ', '.join(fields)







More information about the Python-list mailing list