DatabaseAssistant.py :  » Web-Frameworks » Aquarium » aquarium-2.3 » aquarium » database » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Aquarium 
Aquarium » aquarium 2.3 » aquarium » database » DatabaseAssistant.py
"""This class abstracts and assists in database connectivity."""

__docformat__ = "restructuredtext"

# Created: Fri Nov  3 11:32:55 PST 2000
# Author: Shannon -jj Behrens
# Email: jjinux@users.sourceforge.net
#
# Copyright (c) Shannon -jj Behrens.  All rights reserved.

from aquarium.util.AquariumClass import AquariumClass
from aquarium.util.AutoLoader import AutoLoader
import aquarium.conf.AquariumProperties as properties


class DatabaseAssistant(AquariumClass, AutoLoader):

    """This class abstracts and assists in database connectivity.

    The DatabaseAssistant (or DatabaseAdministrator or DatabaseAutoloader
    or DatabaseAbstractor or DatabaseAdaptor) helps non-database modules
    work with the database.  This is what I have envisioned for database
    access in Aquarium:  I'd like screen modules, etc. to be able to
    say (for example)::

        myWhatever = self._ctx.dba.SomeTable.someQueryTask(someParams)

    This means a couple things.  The only classes that should ever have SQL
    in them or reference the python database module (as defined in
    AquariumProperties) are database modules (i.e. modules located in the
    database directory).  This will create greater flexibility (or at least
    ease the pain) when it is time to change database types or schemas.
    Next, each set of related tasks (usually associated with a particular
    table in the database) will each be accessible via one class that
    basically provides an API for interaction with that table.  Last, all
    the management of importing the various "table classes" should be taken
    care of by Aquarium (via this class or one of its subclasses).  This
    means that whatever class ``dba`` belongs to, in the above example, must
    be able to transparently catch the call to ``SomeTable``, import the
    ``SomeTable`` class, instantiate it, and then call the ``someQueryTask``
    method.  Naturally, we'll be caching the class once it's instantiated.
    Furthermore, ``dba`` should also provide any functions that must be called
    by non-database modules (i.e. it should be an adaptor for the Python
    database modules).  For instance, it should provide a ``connect`` function
    because this function must be called by a non-database module.

    Here's how I see the above taking place.  This class will define a
    base class ``DatabaseAssistant``.  This base class (or one of its
    subclasses) will be responsible for the following:

    * Using the AquariumProperties to import the correct Python database
      module(s) and save a reference in ``ctx.db``.

    * Abstracting any functions defined in the Python database module(s)
      that need to be called by modules other than database modules
      (hence the funny naming convention for the methods in this class):

    * Mixing in the aquarium.util.AutoLoader_ class in order to do dynamic
      imports of the "table classes."

    Once the ``DatabaseAssistant`` it taken care of, then you can define a
    bunch of "table classes" (that decend from aquarium.util.AquariumClass_)
    that use the database assistant as well as the Python database module to
    implement a bunch of functions relating to a particular set of tasks
    (usually associated with a particular table).

    The following private variables are used:

    _connection
      This is our connection to the database.

    .. _aquarium.util.AutoLoader: aquarium.util.AutoLoader.AutoLoader-class.html
    .. _aquarium.util.AquariumClass:
       aquarium.util.AquariumClass.AquariumClass-class.html

    """

    def __init__(self, ctx):
        """Initialize private variables.  Set ``ctx.db``.

        Notice that Python database modules don't contain exactly
        one class (they don't use classes at all), which is a bit
        different from Aquarium modules.

        """
        AquariumClass.__init__(self, ctx)
        module = properties.PYTHON_DATABASE_MODULE
        exec("import %s" % module)
        ctx.db = eval(module)

    def connect(self):
        """Connect to the database.

        Will automatically call close for any previous connections.

        """
        ctx = self._ctx
        if hasattr(self, "_connection"):
            self._close()
        self._connection = ctx.db.connect(
            *properties.DATABASE_CONNECTION_ARGS,
            **properties.DATABASE_CONNECTION_KWARGS)

    def cursor(self):
        """Get a cursor object.

        You'll get a ``NameError`` exception if you try to call this
        before a connection has been made.  That's probably good
        enough.

        """
        return self._connection.cursor()

    def fetchonedict(self, cursor, force=True):
        """Fetch one row as a dict.

        If force is True and there are no more rows, I'll raise a
        MissingRecordError.  Otherwise (if force is False and there are no more
        rows), I'll simply return None.  Note, database modules don't *have* to
        use this function, I'm just providing it for convenience.

        """
        row = cursor.fetchone()
        if not row:
            if force:
                raise MissingRecordError
            else:
                return None
        fieldCount = len(cursor.description)
        retDict = {}
        for i in range(0, fieldCount):
            # Note that the 0'th element of each tuple in
            # cursor.description is the col name.
            retDict[cursor.description[i][0]] = row[i]
        return retDict

    def fetchalldicts(self, cursor):
        """Fetch all rows and return a list of dicts.

        This function will return ``[]`` if there are no more rows
        available.  Note, database modules don't *have* to use
        this function, I'm just providing it for convenience.

        """
        return list(self.fetchalldictsgenerator(cursor))

    def fetchalldictsgenerator(self, cursor):
        """This is a generator for fetching all rows as dicts.

        Note, database modules don't *have* to use this function, I'm just
        providing it for convenience.

        """
        try:
            while True:
                yield self.fetchonedict(cursor)
        except MissingRecordError:
            pass

    def close(self):
        """Close the connection do the database.

        This function is safe to call twice.

        """
        if hasattr(self, "_connection"):
            self._connection.close()
            del(self._connection)

    def commit(self):
        """Commit the current transaction.

        If the database doesn't support transactions, I'll do nothing.

        """
        if hasattr(self._connection, "commit"):
            self._connection.commit()

    def rollback(self):
        """Rollback the current transaction.

        If the database doesn't support transactions, I'll do nothing.

        """
        if hasattr(self._connection, "rollback"):
            self._connection.rollback()

    def __del__(self):
        """Rollback anything uncommitted.  Close the connection."""
        if not hasattr(self, "_connection"):
            return
        self.rollback()
        self.close()


class MissingRecordError(LookupError):

    """A database record that was expected to exist doesn't actually exist."""

    def __init__(self, *args, **kargs):
        """Provide a default error message."""
        if not args:
            args = (self.__init__.__doc__,)
        LookupError.__init__(self, *args, **kargs)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.