DatabaseOperation.py :  » Database » Modeling-Framework » Modeling-0.9 » Modeling » 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 » Database » Modeling Framework 
Modeling Framework » Modeling 0.9 » Modeling » DatabaseOperation.py
# -*- coding: iso-8859-1 -*-
#-----------------------------------------------------------------------------
# Modeling Framework: an Object-Relational Bridge for python
#
# Copyright (c) 2001-2004 Sbastien Bigaret <sbigaret@users.sourceforge.net>
# All rights reserved.
#
# This file is part of the Modeling Framework.
#
# This code is distributed under a "3-clause BSD"-style license;
# see the LICENSE file for details.
#-----------------------------------------------------------------------------

"""
AdaptorOperation API

  Constants

    The following constants (except DATABASE_OPERATORS and ADAPTOR_OPERATORS
    which gather them in two separate lists) identify the operations
    DatabaseOperation and AdaptorOperation instances should perform::

      DATABASE_DELETE_OPERATOR
      DATABASE_INSERT_OPERATOR 
      DATABASE_NOTHING_OPERATOR 
      DATABASE_UPDATE_OPERATOR 
      DATABASE_OPERATORS
  
      ADAPTOR_DELETE_OPERATOR 
      ADAPTOR_INSERT_OPERATOR 
      ADAPTOR_LOCK_OPERATOR 
      ADAPTOR_STOREDPROCEDURE_OPERATOR 
      ADAPTOR_UPDATE_OPERATOR 
      ADAPTOR_OPERATORS

  Why a DATABASE_NOTHING_OPERATOR?

    Suppose you have two entities, 'Writer' and 'Book', with a one-to-many
    relationship between them ; now suppose that we fetched a writer and its
    books, and that the only modification we made was the removal of one of
    these books from the relationship. When it is asked to save the changes,
    the EditingContext already has marked both the writer and the removed book
    as ``updated'' ; however, in a relational database a one-to-many
    relationship only relies upon a single foreign key: in the database, table
    'WRITER' has a foreign key storing the primary key of a Book --the
    relationship itself is said to be stored asymetrically, since only one
    foreign key in a single table holds the necessary information for a
    relationship and its inverse relationship. Hence, even if both the
    'Writer' instance and the removed book are marked as changed/updated by
    the EditingContext, only the database-row corresponding to the removed
    book has to be changed (the corresponding foreign key becomes 'NULL').  A
    DatabaseContext detects that and will promote the DatabaseOperation's
    databaseOperator() for the writer from DATABASE_UPDATE_OPERATOR to
    DATABASE_NOTHING_OPERATOR.

    NB: same for a Writer.toMany relationship->Book, with no inverse
    relationship. This is the very reason why a DatabaseOperation needs to
    store toMany snapshots: in this case, the removed book didn't get notified
    on any changes (this is ok and definitely a normal behaviour since at the
    object level nothing has changed) --> storing the toMany relationships
    allows the DatabaseContext to notify the changes to the other side (see
    ObjectStoreCoordinator.forwardUpdateForObject() and
    DatabaseContext.recordUpdateForObject()).
    
  CVS information

    $Id: DatabaseOperation.py 932 2004-07-20 06:21:57Z sbigaret $

"""
__version__='$Revision: 932 $'[11:-2]

# Constants
DATABASE_DELETE_OPERATOR = 11
DATABASE_INSERT_OPERATOR = 12
DATABASE_NOTHING_OPERATOR = 17
DATABASE_UPDATE_OPERATOR = 15
DATABASE_OPERATORS=(DATABASE_DELETE_OPERATOR,
                    DATABASE_INSERT_OPERATOR,
                    DATABASE_NOTHING_OPERATOR,
                    DATABASE_UPDATE_OPERATOR)

ADAPTOR_LOCK_OPERATOR = 1
ADAPTOR_INSERT_OPERATOR = 2
ADAPTOR_UPDATE_OPERATOR = 3        
ADAPTOR_DELETE_OPERATOR = 4
ADAPTOR_STORED_PROCEDURE_OPERATOR = 5        # unsupported yet
ADAPTOR_OPERATORS=(ADAPTOR_LOCK_OPERATOR,
                   ADAPTOR_INSERT_OPERATOR,
                   ADAPTOR_UPDATE_OPERATOR,
                   ADAPTOR_DELETE_OPERATOR,
                   #ADAPTOR_STORED_PROCEDURE_OPERATOR, 
                   )
#
class DatabaseOperation:
  """
  A DatabaseOperation holds all the informations needed to make changes of
  an object persistent --this can be either the creation of that object, an
  update of its values, or the deletion of that object.

  These informations are:

    - the object itself, its GlobalID and its Entity,
    
    - two kinds of snapshots: one which represent the object as it was last
      fetched or saved to the database, and one which represent its current
      state.
    
  DatabaseOperations are created by a DatabaseContext when it is instructed to
  examine the changes in an EditingContext's graph of objects (see
  DatabaseContext.recordChangesInEditingContext()). After this, and normally
  under the control of an ObjectStoreCoordinator, the DatabaseContext builds
  specific AdaptorOperations for each DatabaseOperations ; the
  AdaptorOperations are then forwarded to the appropriate AdaptorChannel which
  is responsible for passing them to the database.
  """
  
  def __init__(self, aGlobalID, anObject, anEntity):
    """
    Initializer for a DatabaseOperation.

    Raises ValueError if at least one of the arguments are None.
    """
    if not (aGlobalID and anObject and anEntity):
      raise ValueError, 'None of the three arguments should be None' 
    self.__gID=aGlobalID
    self.__object=anObject
    self.__entity=anEntity

    self.__adaptorOperations=[]
    self.__databaseOperator=None
    self.__dbSnapshot={}
    self.__newRow={}
    self.__toManySnaphots={} # relationship's name -> (globalIDs,)

  def adaptorOperations(self):
    """
    Returns the set of AdaptorOperations bound to this DatabaseOperation.
    This set is built by a DatabaseContext.

    See also: addAdaptorOperation(), DatabaseContext.performChanges()
              removeAdaptorOperation()
    """
    return self.__adaptorOperations
  
  def addAdaptorOperation(self, anAdaptorOperation):
    """
    Binds a new AdaptorOperation to the DatabaseOperation().

    See also: adaptorOperations(), DatabaseContext.performChanges(),
              removeAdaptorOperation()
    """
    self.__adaptorOperations.append(anAdaptorOperation)
    
  def databaseOperator(self):
    """
    Returns the database operator.

    See also: DATABASE_OPERATORS, setDatabaseOperator()
    """
    return self.__databaseOperator

  def dbSnapshot(self):
    """
    Returns the snapshot that represents the object as it was last fetched
    from/saved to the database. DatabaseOperations whose operator is
    DATABASE_INSERT_OPERATOR have obviously no such snapshot.
    
    See also: setDBSnapshot() for a complete description of what a snapshot is.
    """
    return self.__dbSnapshot
  
  def entity(self):
    """
    Returns the 'Entity' of the 'object()' this DatabaseOperation handles.
    """
    return self.__entity
  
  def globalID(self):
    """
    Returns the 'GlobalID' of the 'object()' this DatabaseOperation handles.
    """
    return self.__gID

  def newRow(self):
    """
    Returns a snapshot (dictionary) representing the object's current state
    that should be made persistent.

    See also: setNewRow() for a complete description of what a snapshot is.
    """
    return self.__newRow

  def object(self):
    """
    Returns the object the DatabaseOperation is responsible for
    """
    return self.__object
  
  def primaryKeyDiffs(self):
    """
    Returns a dictionary made of changes observed for the primary key(s)
    of the object -- for a full description of this dictionary, see
    rowDiffsForAttributes().

    Returned value is always an empty dictionary if the DatabaseOperation's
    operator() is not DATABASE_UPDATE_OPERATOR.

    Implementation note:

      This method is not used for the moment being. In fact, it is in direct
      relation with the fact that the propagation of the primary key value is
      not supported yet for to-one relationships ; however, this is the *only*
      case where an object's primary key(s) can be changed.

    """
    if self.operator() is not DATABASE_UPDATE_OPERATOR:
      return {}
    return self.rowDiffsForAttributes(self.__entity.primaryKeyAttributes())

  def recordToManySnapshot(self, globalIDs, name):
    """
    Registers the array of GlobalIDs corresponding to the objects that are in
    relation with self.object(), for the supplied relationship.

    Parameters:

      globalIDs -- a sequence made of GlobalIDs, or '0' (integer 0) if the
        relationship is still a fault.

      name -- the name of the corresponding relationship

    See also: recordToManySnapshots(), toManySnapshots()
    """
    self.__toManySnaphots[name]=globalIDs

  def recordToManySnapshots(self, toManySnapshots):
    """
    Registers the toMany snapshots corresponding to the objects that are in
    relation with self.object().

    Parameter:

      snapshots -- an dictionary whose keys are relationships' names, and
        whose values are GlobalIDs sequence (a '0' (integer 0) value
        represents a faulted relationship)

    See also: recordToManySnapshot()
    """
    if toManySnapshots is None: return
    for name in toManySnapshots.keys():
      self.recordToManySnapshot(toManySnapshots[name], name)

  def removeAdaptorOperation(self, anAdaptorOperation):
    """
    Removes an AdaptorOperation from the bound adaptorOperations()
    
    Raises 'ValueError' in case 'anAdaptorOperation' is not in the object's
    'adaptorOperations()'

    Parameter:

      anAdaptorOperation -- an AdaptorOperation object

    See also: adaptorOperations(), addAdaptorOperation()
    """
    self.__adaptorOperations.remove(anAdaptorOperation)

  def rowDiffs(self):
    """
    Returns the set of value that are different between the database napshot
    and the newRow.

    Returned value is a dictionary with attributes' names as keys, associated
    with the new value.
    
    """
    res={}
    for key in self.__dbSnapshot.keys():
      if self.__dbSnapshot[key]!=self.__newRow[key]:
        res[key]=self.__newRow[key]
    return res

  def rowDiffsForAttributes(self, attributes):
    """
    Same as rowDiffs(), but the returned dictionary is restricted to the
    names of the supplied attributes.

    Parameter:

      attributes -- a sequence of Attribute object.
      
    """
    res={}
    for key in map(lambda attr: attr.name(), attributes):
      if self.__dbSnapshot[key]!=self.__newRow[key]:
        res[key]=self.__newRow[key]
    return res

  def setDatabaseOperator(self, aDatabaseOperator):
    """
    Sets the DatabaseOperation's operator.

    Parameter:

      aDatabaseOperator -- one of the 'DATABASE_OPERATORS'
      
    Raises 'ValueError' if 'aDatabaseOperator' is not within
    DATABASE_OPERATORS, or if 'aDatabaseOperator' is
    'DATABASE_INSERT_OPERATOR' (resp. 'DATABASE_DELETE_OPERATOR') and the
    object's databae operator is already set to 'DATABASE_DELETE_OPERATOR'
    (resp. 'DATABASE_INSERT_OPERATOR') [this is to make sure that the
    re-insertion of a previously deleted object has been correctly handled
    within the 'EditingContext', as it should be]

    """
    if aDatabaseOperator not in DATABASE_OPERATORS:
      raise ValueError, 'Invalid database operator'
    if (aDatabaseOperator==DATABASE_INSERT_OPERATOR and \
        self.__databaseOperator==DATABASE_DELETE_OPERATOR) or \
       (aDatabaseOperator==DATABASE_DELETE_OPERATOR and \
        self.__databaseOperator==DATABASE_INSERT_OPERATOR):
      raise ValueError, 'Insert operation cannot be promoted to delete '\
            'operations --and vice-versa'
    self.__databaseOperator=aDatabaseOperator
    
  def setDBSnapshot(self, dbSnapshot):
    """
    Sets the DatabaseOperation's DB snaphost

    Parameter:

      dbSnapshot -- the snapshot of the object as it was when the object was
      last fetched from/committed to the database.

    See also: dbSnapshot()
    """
    self.__dbSnapshot=dbSnapshot

  def setNewRow(self, newRow):
    """
    Sets the rows which reflects the state of the object about to be saved.

    Parameter

      'newRow' -- a dictionary whose keys are the attributes of the object's
      entity (derived attribute shouldn't be included), along with
      name of the entity's to-one relationships, and whose values are,
      unsurprisingly, the corresponding values!

    """
    self.__newRow=newRow
    
  def toManySnapshots(self):
    """

    See also: recordToManySnapshot()
    """
    return self.__toManySnaphots
  
  def __str__(self):
    """
    """
    return "<DBOperation op: %s gid: %s newRow: %s dbSnapshot: %s at %s>"%(self.__databaseOperator, self.__gID, self.__newRow, self.__dbSnapshot, hex(id(self)))

  def __repr__(self):
    return self.__str__()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.