{ "info": { "author": "Malthe Borch, Stefan Eletzhofer and the Zope Community", "author_email": "zope-dev@zope.org", "bugtrack_url": null, "classifiers": [ "Environment :: Web Environment", "Framework :: Zope3", "Programming Language :: Python" ], "description": ".. important:: This project has been retired; another project dobbin_ has taken its place.\r\n\r\n.. _dobbin: http://pypi.python.org/pypi/dobbin\r\n\r\nOverview\r\n========\r\n\r\nDobbin is an object database implemented on top of SQLAlchemy. It's\r\ndesigned to mimick the behavior of the Zope object database (ZODB)\r\nwhile providing greater flexibility and control of the storage.\r\n\r\nIt supports strong typing with native SQL columns by utilizing the\r\ndeclarative field definitions from zope.schema. Weak typing is\r\nsupported using the Python pickle protocol. Attributes are\r\nautomatically persisted with the exception of those starting with the\r\ncharacters \"_v_\" (volatile attributes).\r\n\r\nTables to support the strongly typed attributes are created on-the-fly\r\nwith a 1:1 correspondence to interfaces with no inheritance (base\r\ninterface). As such, objects are modelled as a join between the\r\ninterfaces they implement plus a table that maintains object metadata\r\nand weakly typed instance attributes.\r\n\r\nAuthors\r\n-------\r\n\r\nThis package was designed and implemented by Malthe Borch and Stefan\r\nEletzhofer with parts contributed by Kapil Thangavelu and Laurence\r\nRowe. It's licensed as ZPL.\r\n\r\n\r\nDeveloper documentation\r\n=======================\r\n\r\nObjects are mapped by their specification. Polymorphic attributes are\r\ndeclared as interface attributes; strong typing may be declared using\r\nschema fields; Attributes that are not declared in a schema or\r\ninterface are considered volatile.\r\n\r\nUnique identifiers (UUID)\r\n-------------------------\r\n\r\nA 16-byte unique identification number is used.\r\n\r\nRelations\r\n---------\r\n\r\nPolymorphic attributes are always stored using foreign key\r\nrelations. This is handled transparently by the framework.\r\n\r\nThe target of a relation may be a basic type such as a string,\r\ninteger, tuple or list, or it may be a mapped object.\r\n\r\nThe following fields allow polymorphic relations of any kind with the\r\ntype declared on assignment.\r\n\r\n * zope.schema.Object\r\n * zope.interface.Attribute\r\n\r\nCollections are instrumented objects and may be declared using the\r\nsequence fields:\r\n\r\n * zope.schema.List\r\n * zope.schema.Dict\r\n * zope.schema.Set\r\n\r\nA note on dictionaries: Dictionaries are keyed by (unicode)\r\nstring. Mapped instances may be used as keys in which case a string\r\nrepresentation of the unique instance identifier is used. Dictionaries\r\nsupport polymorphic values with type set on assignment.\r\n\r\n\r\n\r\nWalk-through of the framework\r\n=============================\r\n\r\nThis section demonstrates the functionality of the package.\r\n\r\nIntroduction\r\n------------\r\n\r\nDobbin uses SQLAlchemy's object relational mapper to transparently\r\nstore objects in the database. Objects are persisted in two levels:\r\n\r\nAttributes may correspond directly to a table column in which case we\r\nsay that the attribute is strongly typed. This is the most optimal\r\nway to store data.\r\n\r\nWe may also store attributes that are not mapped directly to a column;\r\nin this case, the value of the attribute is stored as a Python\r\npickle. This allows weak typing, but also persistence of amorphic\r\ndata, e.g. data which does not fit naturally in a relational database.\r\n\r\nA universally unique id (UUID) is automatically assigned to all\r\nobjects.\r\n\r\nWe begin with a new database session.\r\n\r\n >>> import z3c.saconfig\r\n >>> session = z3c.saconfig.Session()\r\n \r\nDeclarative configuration\r\n-------------------------\r\n\r\nWe can map attributes to table columns using zope.schema. Instead of\r\nusing SQL column definitions, we rely on the declarative properties of\r\nschema fields.\r\n\r\nWe start out with an interface decribing a recorded album.\r\n\r\n >>> class IAlbum(interface.Interface):\r\n ... artist = schema.TextLine(\r\n ... title=u\"Artist\",\r\n ... default=u\"\")\r\n ...\r\n ... title = schema.TextLine(\r\n ... title=u\"Title\",\r\n ... default=u\"\")\r\n\r\nWe can now fabricate instances that implement this interface by using\r\nthe ``create`` method. This is a shorthand for setting up a mapper and\r\ncreating an instance by calling it.\r\n\r\n >>> from z3c.dobbin.factory import create\r\n >>> album = create(IAlbum)\r\n\r\nSet attributes.\r\n \r\n >>> album.artist = \"The Beach Boys\"\r\n >>> album.title = u\"Pet Sounds\"\r\n \r\nInterface inheritance is supported. For instance, a vinyl record is a\r\nparticular type of album.\r\n\r\n >>> class IVinyl(IAlbum):\r\n ... rpm = schema.Int(\r\n ... title=u\"RPM\")\r\n\r\n >>> vinyl = create(IVinyl)\r\n >>> vinyl.artist = \"Diana Ross and The Supremes\"\r\n >>> vinyl.title = \"Taking Care of Business\"\r\n >>> vinyl.rpm = 45\r\n\r\nThe attributes are instrumented by SQLAlchemy and map directly to a\r\ncolumn in a table.\r\n\r\n >>> IVinyl.__mapper__.artist\r\n \r\n\r\nA compact disc is another kind of album.\r\n\r\n >>> class ICompactDisc(IAlbum):\r\n ... year = schema.Int(title=u\"Year\")\r\n\r\nLet's pick a more recent Diana Ross, to fit the format.\r\n \r\n >>> cd = create(ICompactDisc)\r\n >>> cd.artist = \"Diana Ross\"\r\n >>> cd.title = \"The Great American Songbook\"\r\n >>> cd.year = 2005\r\n \r\nTo verify that we've actually inserted objects to the database, we\r\ncommit the transacation, thus flushing the current session.\r\n\r\n >>> session.save(album)\r\n >>> session.save(vinyl)\r\n >>> session.save(cd)\r\n\r\nWe must actually query the database once before proceeding; this seems\r\nto be a bug in ``zope.sqlalchemy``.\r\n \r\n >>> results = session.query(album.__class__).all()\r\n\r\nProceed with the transaction.\r\n \r\n >>> import transaction\r\n >>> transaction.commit()\r\n\r\nWe get a reference to the database metadata object, to locate each\r\nunderlying table.\r\n \r\n >>> engine = session.bind\r\n >>> metadata = engine.metadata\r\n\r\nTables are given a name based on the dotted path of the interface they\r\ndescribe. A utility method is provided to create a proper table name\r\nfor an interface.\r\n \r\n >>> from z3c.dobbin.mapper import encode\r\n\r\nVerify tables for ``IVinyl``, ``IAlbum`` and ``ICompactDisc``.\r\n \r\n >>> session.bind = metadata.bind\r\n >>> session.execute(metadata.tables[encode(IVinyl)].select()).fetchall()\r\n [(2, 45)]\r\n\r\n >>> session.execute(metadata.tables[encode(IAlbum)].select()).fetchall()\r\n [(1, u'Pet Sounds', u'The Beach Boys'),\r\n (2, u'Taking Care of Business', u'Diana Ross and The Supremes'),\r\n (3, u'The Great American Songbook', u'Diana Ross')]\r\n\r\n >>> session.execute(metadata.tables[encode(ICompactDisc)].select()).fetchall()\r\n [(3, 2005)]\r\n\r\nMapping concrete classes\r\n------------------------\r\n \r\nNow we'll create a mapper based on a concrete class. We'll let the\r\nclass implement the interface that describes the attributes we want to\r\nstore, but also provides a custom method.\r\n\r\n >>> class Vinyl(object):\r\n ... interface.implements(IVinyl)\r\n ...\r\n ... def __repr__(self):\r\n ... return \"\" % \\\r\n ... (self.artist, self.title, self.rpm)\r\n\r\nAlthough the symbols we define in this test report that they're\r\navailable from the ``__builtin__`` module, they really aren't.\r\n\r\nWe'll manually add these symbols.\r\n\r\n >>> import __builtin__\r\n >>> __builtin__.IVinyl = IVinyl\r\n >>> __builtin__.IAlbum = IAlbum\r\n >>> __builtin__.Vinyl = Vinyl\r\n\r\nCreate an instance using the ``create`` factory.\r\n \r\n >>> vinyl = create(Vinyl)\r\n\r\nVerify that we've instantiated and instance of our class.\r\n \r\n >>> isinstance(vinyl, Vinyl)\r\n True\r\n\r\nCopy the attributes from the Diana Ross vinyl record.\r\n\r\n >>> diana = session.query(IVinyl.__mapper__).filter_by(\r\n ... artist=u\"Diana Ross and The Supremes\")[0]\r\n >>> vinyl.artist = diana.artist\r\n >>> vinyl.title = diana.title\r\n >>> vinyl.rpm = diana.rpm\r\n\r\nVerify that the methods on our ``Vinyl``-class are available on the mapper.\r\n\r\n >>> repr(vinyl)\r\n ''\r\n\r\nWhen mapping a class we may run into properties that should take the\r\nplace of a column (a read-only value). As an example, consider this\r\nexperimental record class where rotation speed is a function of the\r\ntitle and artist.\r\n\r\n >>> class Experimental(Vinyl):\r\n ... @property\r\n ... def rpm(self):\r\n ... return len(self.title+self.artist)\r\n\r\n >>> experimental = create(Experimental)\r\n\r\nXXX: There's currently an issue with SQLAlchemy that hinders this\r\nbehavior; it specifically won't work if a default value is set on the\r\ncolumn that we're overriding.\r\n\r\n >>> # session.save(experimental)\r\n \r\n >>> experimental.artist = vinyl.artist\r\n >>> experimental.title = vinyl.title\r\n\r\nLet's see how fast this record should be played back.\r\n\r\n >>> experimental.rpm\r\n 50\r\n\r\nRelations\r\n---------\r\n\r\nRelations are columns that act as references to other objects. They're\r\ndeclared using the ``zope.schema.Object`` field.\r\n\r\nNote that we needn't declare the relation target type in advance,\r\nalthough it may be useful in general to specialize the ``schema``\r\nkeyword parameter.\r\n\r\n >>> class IFavorite(interface.Interface):\r\n ... item = schema.Object(\r\n ... title=u\"Item\",\r\n ... schema=interface.Interface)\r\n\r\n >>> __builtin__.IFavorite = IFavorite\r\n \r\nLet's make our Diana Ross record a favorite.\r\n\r\n >>> favorite = create(IFavorite)\r\n >>> favorite.item = vinyl\r\n >>> favorite.item\r\n \r\n\r\n >>> session.save(favorite)\r\n\r\nWe'll commit the transaction and lookup the object by its unique id.\r\n\r\n >>> transaction.commit()\r\n\r\n >>> from z3c.dobbin.soup import lookup\r\n >>> favorite = lookup(favorite.uuid)\r\n \r\nWhen we retrieve the related items, it's automatically reconstructed\r\nto match the specification to which it was associated.\r\n\r\n >>> favorite.item\r\n \r\n\r\nWe can create relations to objects that are not mapped. Let's model an\r\naccessory item.\r\n\r\n >>> class IAccessory(interface.Interface):\r\n ... name = schema.TextLine(title=u\"Name of accessory\")\r\n\r\n >>> class Accessory(object):\r\n ... interface.implements(IAccessory)\r\n ...\r\n ... def __repr__(self):\r\n ... return \"\" % self.name\r\n\r\nIf we now instantiate an accessory and assign it as a favorite item,\r\nwe'll implicitly create a mapper from the class specification and\r\ninsert it into the database.\r\n\r\n >>> cleaner = Accessory()\r\n >>> cleaner.name = u\"Record cleaner\"\r\n\r\nSet up relation.\r\n \r\n >>> favorite.item = cleaner \r\n\r\nLet's try and get back our record cleaner item.\r\n\r\n >>> __builtin__.Accessory = Accessory\r\n >>> favorite.item\r\n \r\n\r\nWithin the same transaction, the relation will return the original\r\nobject, maintaining integrity.\r\n\r\n >>> favorite.item is cleaner\r\n True\r\n\r\nThe session keeps a copy of the pending object until the transaction\r\nis ended.\r\n\r\n >>> cleaner in session._d_pending.values()\r\n True\r\n\r\nHowever, once we commit the transaction, the relation is no longer\r\nattached to the relation source, and the correct data will be\r\npersisted in the database.\r\n\r\n >>> cleaner.name = u\"CD cleaner\"\r\n \r\n >>> session.flush()\r\n >>> session.update(favorite)\r\n \r\n >>> favorite.item.name\r\n u'CD cleaner'\r\n \r\nThis behavior should work well in a request-response type environment,\r\nwhere the request will typically end with a commit.\r\n\r\nCollections\r\n-----------\r\n\r\nWe can instrument properties that behave like collections by using the\r\nsequence and mapping schema fields.\r\n\r\nLet's set up a record collection as an ordered list.\r\n\r\n >>> class ICollection(interface.Interface):\r\n ... records = schema.List(\r\n ... title=u\"Records\",\r\n ... value_type=schema.Object(schema=IAlbum)\r\n ... )\r\n\r\n >>> __builtin__.ICollection = ICollection\r\n \r\n >>> collection = create(ICollection)\r\n >>> collection.records\r\n []\r\n\r\nAdd the Diana Ross record, and save the collection to the session.\r\n\r\n >>> collection.records.append(diana)\r\n >>> session.save(collection)\r\n \r\nWe can get our collection back.\r\n\r\n >>> collection = lookup(collection.uuid)\r\n\r\nLet's verify that we've stored the Diana Ross record.\r\n \r\n >>> record = collection.records[0]\r\n \r\n >>> record.artist, record.title\r\n (u'Diana Ross and The Supremes', u'Taking Care of Business')\r\n\r\n >>> session.flush()\r\n \r\nWhen we create a new, transient object and append it to a list, it's\r\nautomatically saved on the session.\r\n\r\n >>> collection = lookup(collection.uuid)\r\n\r\n >>> kool = create(IVinyl)\r\n >>> kool.artist = u\"Kool & the Gang\"\r\n >>> kool.title = u\"Music Is the Message\"\r\n >>> kool.rpm = 33\r\n \r\n >>> collection.records.append(kool)\r\n >>> [record.artist for record in collection.records]\r\n [u'Diana Ross and The Supremes', u'Kool & the Gang']\r\n\r\n >>> session.flush()\r\n >>> session.update(collection)\r\n\r\nWe can remove items.\r\n\r\n >>> collection.records.remove(kool)\r\n >>> len(collection.records) == 1\r\n True\r\n\r\nAnd extend.\r\n\r\n >>> collection.records.extend((kool,))\r\n >>> len(collection.records) == 2\r\n True\r\n\r\nItems can appear twice in the list.\r\n\r\n >>> collection.records.append(kool)\r\n >>> len(collection.records) == 3\r\n True\r\n\r\nWe can add concrete instances to collections.\r\n\r\n >>> marvin = Vinyl()\r\n >>> marvin.artist = u\"Marvin Gaye\"\r\n >>> marvin.title = u\"Let's get it on\"\r\n >>> marvin.rpm = 33\r\n \r\n >>> collection.records.append(marvin)\r\n >>> len(collection.records) == 4\r\n True\r\n\r\nAnd remove them, too.\r\n\r\n >>> collection.records.remove(marvin)\r\n >>> len(collection.records) == 3\r\n True\r\n\r\nThe standard list methods are available.\r\n\r\n >>> collection.records = [marvin, vinyl]\r\n >>> collection.records.sort(key=lambda record: record.artist)\r\n >>> collection.records\r\n [,\r\n ]\r\n\r\n >>> collection.records.reverse()\r\n >>> collection.records\r\n [,\r\n ]\r\n\r\n >>> collection.records.index(vinyl)\r\n 1\r\n \r\n >>> collection.records.pop()\r\n \r\n\r\n >>> collection.records.insert(0, vinyl)\r\n >>> collection.records\r\n [,\r\n ]\r\n\r\n >>> collection.records.count(vinyl)\r\n 1\r\n\r\n >>> collection.records[1] = vinyl\r\n >>> collection.records.count(vinyl)\r\n 2\r\n \r\nFor good measure, let's create a new instance without adding any\r\nelements to its list.\r\n\r\n >>> empty_collection = create(ICollection)\r\n >>> session.save(empty_collection)\r\n\r\nTo demonstrate the mapping implementation, let's set up a catalog for\r\nour record collection. We'll index the records by their ASIN string.\r\n \r\n >>> class ICatalog(interface.Interface):\r\n ... index = schema.Dict(\r\n ... title=u\"Record index\")\r\n \r\n >>> catalog = create(ICatalog)\r\n >>> session.save(catalog)\r\n\r\nAdd a record to the index.\r\n \r\n >>> catalog.index[u\"B00004WZ5Z\"] = diana\r\n >>> catalog.index[u\"B00004WZ5Z\"]\r\n \r\n\r\nVerify state after commit.\r\n \r\n >>> transaction.commit()\r\n >>> catalog.index[u\"B00004WZ5Z\"]\r\n \r\n\r\nLet's check that the standard dict methods are supported.\r\n\r\n >>> catalog.index.values()\r\n []\r\n\r\n >>> tuple(catalog.index.itervalues())\r\n (,)\r\n\r\n >>> catalog.index.setdefault(u\"B00004WZ5Z\", None)\r\n \r\n\r\n >>> catalog.index.pop(u\"B00004WZ5Z\")\r\n \r\n\r\n >>> len(catalog.index)\r\n 0\r\n\r\nConcrete instances are supported.\r\n\r\n >>> vinyl = Vinyl()\r\n >>> vinyl.artist = diana.artist\r\n >>> vinyl.title = diana.title\r\n >>> vinyl.rpm = diana.rpm\r\n\r\n >>> catalog.index[u\"B00004WZ5Z\"] = vinyl\r\n >>> len(catalog.index)\r\n 1\r\n\r\n >>> catalog.index.popitem()\r\n (u'B00004WZ5Z',\r\n )\r\n\r\n >>> catalog.index = {u\"B00004WZ5Z\": vinyl}\r\n >>> len(catalog.index)\r\n 1\r\n\r\n >>> catalog.index.clear()\r\n >>> len(catalog.index)\r\n 0\r\n\r\nWe may use a mapped object as index.\r\n\r\n >>> catalog.index[diana] = diana\r\n >>> catalog.index.keys()[0] == diana.uuid\r\n True\r\n\r\n >>> transaction.commit()\r\n \r\n >>> catalog.index[diana] \r\n \r\n \r\n >>> class IDiscography(ICatalog):\r\n ... records = schema.Dict(\r\n ... title=u\"Discographies by artist\",\r\n ... value_type=schema.List())\r\n\r\nAmorphic objects\r\n----------------\r\n\r\nWe can set and retrieve attributes that aren't declared in an\r\ninterface.\r\n\r\n >>> record = create(interface.Interface)\r\n\r\n >>> record.publisher = u\"Columbia records\"\r\n >>> record.publisher\r\n u'Columbia records'\r\n\r\n >>> session.save(record)\r\n >>> session.query(record.__class__).filter_by(\r\n ... uuid=record.uuid)[0].publisher\r\n u'Columbia records'\r\n \r\nUsing this kind of weak we can store (almost) any kind of\r\nstructure. Values are kept as Python pickles.\r\n\r\n >>> favorite = create(interface.Interface)\r\n >>> session.save(favorite)\r\n\r\nA transaction hook makes sure that assigned values are transient\r\nduring a session.\r\n \r\n >>> obj = object()\r\n >>> favorite.item = obj\r\n >>> favorite.item is obj\r\n True\r\n \r\nIntegers, floats and unicode strings are straight-forward.\r\n \r\n >>> favorite.item = 42; transaction.commit()\r\n >>> favorite.item\r\n 42\r\n\r\n >>> favorite.item = 42.01; transaction.commit()\r\n >>> 42 < favorite.item <= 42.01\r\n True\r\n\r\n >>> favorite.item = u\"My favorite number is 42.\"; transaction.commit()\r\n >>> favorite.item\r\n u'My favorite number is 42.'\r\n\r\nNormal strings need explicit coercing to ``str``.\r\n \r\n >>> favorite.item = \"My favorite number is 42.\"; transaction.commit()\r\n >>> str(favorite.item)\r\n 'My favorite number is 42.'\r\n\r\nOr sequences of items.\r\n\r\n >>> favorite.item = (u\"green\", u\"blue\", u\"red\"); transaction.commit()\r\n >>> favorite.item\r\n (u'green', u'blue', u'red')\r\n\r\nDictionaries.\r\n\r\n >>> favorite.item = {u\"green\": 0x00FF00, u\"blue\": 0x0000FF, u\"red\": 0xFF0000}\r\n >>> transaction.commit()\r\n >>> favorite.item\r\n {u'blue': 255, u'green': 65280, u'red': 16711680}\r\n\r\n >>> favorite.item[u\"black\"] = 0x000000\r\n >>> sorted(favorite.item.items())\r\n [(u'black', 0), (u'blue', 255), (u'green', 65280), (u'red', 16711680)]\r\n\r\nWe do need explicitly set the dirty bit of this instance.\r\n\r\n >>> favorite.item = favorite.item\r\n >>> transaction.commit()\r\n\r\n >>> sorted(favorite.item.items())\r\n [(u'black', 0), (u'blue', 255), (u'green', 65280), (u'red', 16711680)]\r\n\r\nWhen we create relations to mutable objects, a hook is made into the\r\ntransaction machinery to keep track of the pending state.\r\n\r\n >>> some_list = [u\"green\", u\"blue\"]\r\n >>> favorite.item = some_list\r\n >>> some_list.append(u\"red\"); transaction.commit()\r\n >>> favorite.item\r\n [u'green', u'blue', u'red']\r\n\r\nAmorphic structures.\r\n\r\n >>> favorite.item = ((1, u\"green\"), (2, u\"blue\"), (3, u\"red\")); transaction.commit()\r\n >>> favorite.item\r\n ((1, u'green'), (2, u'blue'), (3, u'red'))\r\n\r\nStructures involving relations to other instances.\r\n\r\n >>> favorite.item = vinyl; transaction.commit()\r\n >>> favorite.item\r\n \r\n\r\nSelf-referencing works because polymorphic attributes are lazy.\r\n\r\n >>> favorite.item = favorite; transaction.commit()\r\n >>> favorite.item\r\n \r\n \r\nSecurity\r\n--------\r\n\r\nThe security model from Zope is applied to mappers.\r\n\r\n >>> from zope.security.checker import getCheckerForInstancesOf\r\n\r\nOur ``Vinyl`` class does not have a security checker defined.\r\n \r\n >>> from z3c.dobbin.mapper import getMapper\r\n >>> mapper = getMapper(Vinyl)\r\n \r\n >>> getCheckerForInstancesOf(mapper) is None\r\n True\r\n\r\nLet's set a checker and regenerate the mapper.\r\n\r\n >>> from zope.security.checker import defineChecker, CheckerPublic\r\n >>> defineChecker(Vinyl, CheckerPublic)\r\n \r\n >>> from z3c.dobbin.mapper import createMapper\r\n >>> mapper = createMapper(Vinyl)\r\n >>> getCheckerForInstancesOf(mapper) is CheckerPublic\r\n True \r\n \r\nKnown limitations\r\n-----------------\r\n\r\nCertain names are disallowed, and will be ignored when constructing\r\nthe mapper.\r\n\r\n >>> class IKnownLimitations(interface.Interface):\r\n ... __name__ = schema.TextLine()\r\n\r\n >>> from z3c.dobbin.interfaces import IMapper\r\n >>> mapper = IMapper(IKnownLimitations)\r\n >>> mapper.__name__\r\n 'Mapper'\r\n\r\nCleanup\r\n-------\r\n \r\nCommit session.\r\n \r\n >>> transaction.commit()\r\n\r\nChange log\r\n==========\r\n\r\n0.4dev\r\n------\r\n\r\n0.4.2\r\n-----\r\n\r\n- Compatibility with SQLAlchemy 0.5rc1.\r\n\r\n0.4.1\r\n-----\r\n\r\n- Fixed version issue.\r\n\r\n0.4.0\r\n-----\r\n\r\n- Added patch to support old-style mixin classes in the inheritance\r\n tree of a mapper class.\r\n\r\n- All attributes that are not declared as interface names are now\r\n persisted automatically using the Pickle-protocol. The exception to\r\n this rule is attributes starting with the characters \"_v_\" (volatile\r\n attributes).\r\n\r\n- Changed target to SQLAlchemy 0.5-series.\r\n\r\n0.3.2\r\n-----\r\n\r\n- Use pickles to store polymorphic attributes; there's no benefit in\r\n using native columns for amorphic data.\r\n\r\n- Dobbin now uses ``zope.sqlalchemy`` for transaction and session\r\n glue.\r\n\r\n0.3.1\r\n-----\r\n\r\n- Use native UUID column type (available on PostgreSQL); compatibility\r\n with SQLite is preserved due to its weak typing.\r\n\r\n\r\n- Basic type factories are now registered as components.\r\n\r\n0.3.0\r\n-----\r\n\r\n- Implemented rest of list methods.\r\n \r\n- Refactoring of table bootstrapping; internal tables now using a\r\n naming convention less likely to clash with existing tables.\r\n\r\n- Added support for ``schema.Dict`` (including polymorphic dictionary\r\n relation).\r\n\r\n- Implemented polymorphic relations for a subset of the basic types\r\n (int, str, unicode, tuple and list).\r\n \r\n0.2.9\r\n-----\r\n\r\n- Tables are now only created once per minimal interface; this fixes\r\n issue on both SQLite and Postgres when we create mappers with an\r\n explicit polymorphic class.\r\n\r\n- First entry in change-log.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "UNKNOWN", "keywords": "zope orm persistence", "license": "ZPL", "maintainer": "", "maintainer_email": "", "name": "z3c.dobbin", "package_url": "https://pypi.org/project/z3c.dobbin/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/z3c.dobbin/", "project_urls": { "Download": "UNKNOWN", "Homepage": "UNKNOWN" }, "release_url": "https://pypi.org/project/z3c.dobbin/0.4.2/", "requires_dist": null, "requires_python": null, "summary": "Relational object persistance framework", "version": "0.4.2" }, "last_serial": 802016, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "023c434c24918751feb23cae37640cfb", "sha256": "5c8cd9cb8507aecaffc5d13f46d366f2d526db2836df428505a873d2f8d29f53" }, "downloads": -1, "filename": "z3c.dobbin-0.1.tar.gz", "has_sig": false, "md5_digest": "023c434c24918751feb23cae37640cfb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11774, "upload_time": "2008-05-06T15:12:04", "url": "https://files.pythonhosted.org/packages/71/ae/026f40e4389fb0d533a12106b4f2ab076e33ce90c5a8595eb619aa80e19d/z3c.dobbin-0.1.tar.gz" } ], "0.2": [ { "comment_text": "", "digests": { "md5": "aed373db214783ab9a4a7e5eb3d67553", "sha256": "d572fd65559e0400ac0e89c39eba12c5b5b32fff935f5f5f91a125331964e3fb" }, "downloads": -1, "filename": "z3c.dobbin-0.2.tar.gz", "has_sig": false, "md5_digest": "aed373db214783ab9a4a7e5eb3d67553", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14182, "upload_time": "2008-05-18T14:08:43", "url": "https://files.pythonhosted.org/packages/e4/63/c41dcce311e8ead5ba4049113edc53e0c175c0530ca1849b36d4449d926d/z3c.dobbin-0.2.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "fd801ae4673da2fed349ebea866abe2b", "sha256": "5c4d533351069090f8b2ccfd4c54a40f49ac9d620e652c32e40934b8f36f86e7" }, "downloads": -1, "filename": "z3c.dobbin-0.2.1.tar.gz", "has_sig": false, "md5_digest": "fd801ae4673da2fed349ebea866abe2b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14188, "upload_time": "2008-05-19T22:01:50", "url": "https://files.pythonhosted.org/packages/e9/ab/39273541fbb85fe2c7bd08158748eb2d0db7c7db9a0d85e837bb728a6064/z3c.dobbin-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "f77ae58870f9f9d4ef01bc583ccdc724", "sha256": "34badd38d73340c6f86d11a6478ceab790c861de22c2a6cc8c2389156c6ceb72" }, "downloads": -1, "filename": "z3c.dobbin-0.2.2.tar.gz", "has_sig": false, "md5_digest": "f77ae58870f9f9d4ef01bc583ccdc724", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14303, "upload_time": "2008-05-21T13:29:15", "url": "https://files.pythonhosted.org/packages/fd/14/f7fa0f7d88890c07541ece67dd3b67337d1d19eeef418b721fd810bcbe6b/z3c.dobbin-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "44aa5484b47db638e00d90954f0c951e", "sha256": "cb72ab7e06bbf38c9489e1cb026468d237fa91b02c892a6dd32f73986f100d50" }, "downloads": -1, "filename": "z3c.dobbin-0.2.3.tar.gz", "has_sig": false, "md5_digest": "44aa5484b47db638e00d90954f0c951e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14481, "upload_time": "2008-05-21T17:15:31", "url": "https://files.pythonhosted.org/packages/ba/99/fcf9660df239269908e94b45504d24fd2c5e5c2ef334eab252e9bda6868e/z3c.dobbin-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "8addd12cf1c5e4054f783860b0413ca3", "sha256": "73302eb2e20b32ec779b06e80afa14e8e1cc80a7d869e84a6ebef60779ad2b05" }, "downloads": -1, "filename": "z3c.dobbin-0.2.4.tar.gz", "has_sig": false, "md5_digest": "8addd12cf1c5e4054f783860b0413ca3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14587, "upload_time": "2008-05-21T21:10:06", "url": "https://files.pythonhosted.org/packages/77/f2/d0722d06745fd3a94e19f4a84d0ff33f2932100c3ef104012bce9f780403/z3c.dobbin-0.2.4.tar.gz" } ], "0.2.5": [ { "comment_text": "", "digests": { "md5": "c5dfcc8497afbc8f741a5f87d833487b", "sha256": "c70ff155abf62daa63c06266e082e4bcf2f1ef18a583db1493740b0daa26aad0" }, "downloads": -1, "filename": "z3c.dobbin-0.2.5.tar.gz", "has_sig": false, "md5_digest": "c5dfcc8497afbc8f741a5f87d833487b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14600, "upload_time": "2008-05-26T15:25:47", "url": "https://files.pythonhosted.org/packages/ed/ac/81817babff07c92395280c8bfe2f8548393f57b50d0a656e26b26b428683/z3c.dobbin-0.2.5.tar.gz" } ], "0.2.6": [ { "comment_text": "", "digests": { "md5": "cf7d285d05f0d9bec61b01c579b64e3a", "sha256": "6242a48a0d4f85b93c9e8ae14d5f32ece5d58c1b08774f870119a36f9ea2e81f" }, "downloads": -1, "filename": "z3c.dobbin-0.2.6.tar.gz", "has_sig": false, "md5_digest": "cf7d285d05f0d9bec61b01c579b64e3a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15080, "upload_time": "2008-05-27T15:46:49", "url": "https://files.pythonhosted.org/packages/de/57/3d964dc6f85eb5b708e96eefd5d9b93b70b6f32e0ae1e31828adf8883f16/z3c.dobbin-0.2.6.tar.gz" } ], "0.2.7": [ { "comment_text": "", "digests": { "md5": "d347f7a754299bdd728b67bbe6a54726", "sha256": "09f9aace67422fc44aa5a459bfc9d67a96f6e3695c20583ffe75399c753b09af" }, "downloads": -1, "filename": "z3c.dobbin-0.2.7.tar.gz", "has_sig": false, "md5_digest": "d347f7a754299bdd728b67bbe6a54726", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15615, "upload_time": "2008-06-11T14:46:23", "url": "https://files.pythonhosted.org/packages/a7/90/04a5d2d7e7b8e584b344543cdf0990f492fb1e3fc5a339ffff5475c91482/z3c.dobbin-0.2.7.tar.gz" } ], "0.2.8": [ { "comment_text": "", "digests": { "md5": "cfa432062c54b4023bdc89d9e7b579e8", "sha256": "314b49145e5ea7dafb35d1ba62f60dbc80ac373bf0b2f262699039ab04d60af7" }, "downloads": -1, "filename": "z3c.dobbin-0.2.8.tar.gz", "has_sig": false, "md5_digest": "cfa432062c54b4023bdc89d9e7b579e8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15953, "upload_time": "2008-06-12T16:06:44", "url": "https://files.pythonhosted.org/packages/9f/45/2a1e7b9597922f8d905c742c806261565ca47af291887621811f35233ab4/z3c.dobbin-0.2.8.tar.gz" } ], "0.2.9": [ { "comment_text": "", "digests": { "md5": "bc245712523205b58980fccfc6ac844c", "sha256": "6d7c8aaf2e0ebf6fe9c67db9819e9663709ba319ce49f9bc86f18bcb2db158b7" }, "downloads": -1, "filename": "z3c.dobbin-0.2.9.tar.gz", "has_sig": false, "md5_digest": "bc245712523205b58980fccfc6ac844c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16141, "upload_time": "2008-06-18T16:48:12", "url": "https://files.pythonhosted.org/packages/98/c5/861ae8c2abab81d937beb384d30e259e430706cb0d9fc1ef9f41be1471ad/z3c.dobbin-0.2.9.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "efb63c97bf245b75e0ea70157ab7048d", "sha256": "ed16597c03bf50ba0ecc32162ffd9cbc8c587d033d155ce3cc684cadd4e04083" }, "downloads": -1, "filename": "z3c.dobbin-0.3.0.tar.gz", "has_sig": false, "md5_digest": "efb63c97bf245b75e0ea70157ab7048d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25294, "upload_time": "2008-06-22T01:02:58", "url": "https://files.pythonhosted.org/packages/02/33/a6cbe0c162c79eab51fd68d813d27a934e3077ac72d703d06d7b5fc6c916/z3c.dobbin-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "a2641eb78765de2f02edbb8cf88d43d9", "sha256": "47ef24b10a6c649a9a681d056bf08ee973fbabfd45acda02178bffa8b76a7aec" }, "downloads": -1, "filename": "z3c.dobbin-0.3.1.tar.gz", "has_sig": false, "md5_digest": "a2641eb78765de2f02edbb8cf88d43d9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25476, "upload_time": "2008-07-02T21:58:51", "url": "https://files.pythonhosted.org/packages/d6/ad/d0c101e201367b16a0e8796dda2050b369d5a1cf28fce239ba7fbd1ac343/z3c.dobbin-0.3.1.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "c6006ba0420452406ced4fd9967bfb13", "sha256": "01c478167f252ebafecd729f7d742f5f38b02c39e4d82fd7a5cf57fcfb45372e" }, "downloads": -1, "filename": "z3c.dobbin-0.3.2.tar.gz", "has_sig": false, "md5_digest": "c6006ba0420452406ced4fd9967bfb13", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27819, "upload_time": "2008-07-17T16:55:41", "url": "https://files.pythonhosted.org/packages/6b/ae/6ff061f21e117522bcfbdfb1fd4415b40561b10215552d970187c1372314/z3c.dobbin-0.3.2.tar.gz" } ], "0.3.3": [ { "comment_text": "", "digests": { "md5": "b0ab6a42ad4f451b6fa04fed099b5be4", "sha256": "16deb612c9d6836b9e5cbe3729ea7bdac404430ff2dfeb510ed26f04a1851145" }, "downloads": -1, "filename": "z3c.dobbin-0.3.3.tar.gz", "has_sig": false, "md5_digest": "b0ab6a42ad4f451b6fa04fed099b5be4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27880, "upload_time": "2008-07-18T12:54:32", "url": "https://files.pythonhosted.org/packages/3f/ca/2e21dc87146b276fa6d91b77e3dec7c1aff3a7a00d3a01fa91be0e6a0977/z3c.dobbin-0.3.3.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "549e99fbaa0222d39199150ecdc7ffcc", "sha256": "befa140d199d96f5f2b21cfba4942ee29ddb3793905ac309885503a6e5e904f8" }, "downloads": -1, "filename": "z3c.dobbin-0.4.0.tar.gz", "has_sig": false, "md5_digest": "549e99fbaa0222d39199150ecdc7ffcc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30068, "upload_time": "2008-07-23T10:39:22", "url": "https://files.pythonhosted.org/packages/ef/d2/c589b6fe224afea2206dc2fb640cdf1bd3eddde7cb71d8e9dbf71c1a8948/z3c.dobbin-0.4.0.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "256128906e05abb1046ff92b176f72b7", "sha256": "4d863c8067cb203a251f53f033469beb7f81d0993e8033c423a0bbcbe9d21d6b" }, "downloads": -1, "filename": "z3c.dobbin-0.4.1.tar.gz", "has_sig": false, "md5_digest": "256128906e05abb1046ff92b176f72b7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30109, "upload_time": "2008-07-30T10:12:57", "url": "https://files.pythonhosted.org/packages/d8/3e/e08b097606f8cfccb82ee657f523249f08a469bd899890c791f66a1668f0/z3c.dobbin-0.4.1.tar.gz" } ], "0.4.2": [ { "comment_text": "", "digests": { "md5": "29833736c0552f98deeee9f7851a5875", "sha256": "6a77b0cf0bf0cb453fc114312decf8706f5dfb59722a64cfa061363f4d78c3e2" }, "downloads": -1, "filename": "z3c.dobbin-0.4.2.tar.gz", "has_sig": false, "md5_digest": "29833736c0552f98deeee9f7851a5875", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30219, "upload_time": "2008-09-19T14:58:42", "url": "https://files.pythonhosted.org/packages/98/5e/88e88dfffd796e782c13a882cdbbdbb845f23d5310b99a5b00c553b81071/z3c.dobbin-0.4.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "29833736c0552f98deeee9f7851a5875", "sha256": "6a77b0cf0bf0cb453fc114312decf8706f5dfb59722a64cfa061363f4d78c3e2" }, "downloads": -1, "filename": "z3c.dobbin-0.4.2.tar.gz", "has_sig": false, "md5_digest": "29833736c0552f98deeee9f7851a5875", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30219, "upload_time": "2008-09-19T14:58:42", "url": "https://files.pythonhosted.org/packages/98/5e/88e88dfffd796e782c13a882cdbbdbb845f23d5310b99a5b00c553b81071/z3c.dobbin-0.4.2.tar.gz" } ] }