Skip to content

Extension of Limone for working with content objects that will be persisted in the ZODB.

Notifications You must be signed in to change notification settings

chrisrossi/limone_zodb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Limone ZODB

Limone ZODB is an extension of Limone which generates content types that are persistable via ZODB. Usage is the same as Limone except that the decorators and make_content_type function are imported from the limone_zodb package instead of the limone package:

import colander
import limone_zodb
import persistent

class Friend(colander.TupleSchema):
    rank = colander.SchemaNode(colander.Int(),
                              validator=colander.Range(0, 9999))
    name = colander.SchemaNode(colander.String())

class Phone(colander.MappingSchema):
    location = colander.SchemaNode(colander.String(),
                                  validator=colander.OneOf(['home', 'work']))
    number = colander.SchemaNode(colander.String())

class Friends(colander.SequenceSchema):
    friend = Friend()

class Phones(colander.SequenceSchema):
    phone = Phone()

@limone_zodb.content_schema
class Person(colander.MappingSchema):
    name = colander.SchemaNode(colander.String())
    age = colander.SchemaNode(colander.Int(),
                             validator=colander.Range(0, 200))
    friends = Friends()
    phones = Phones()

jake = Person(name='Jake', age=21)
assert isinstance(jake, persistent.Persistent)

Rationale

Naively, one might presume that using persistent.Persistent as a base class for the content type would be sufficient. What can happen, though, is you can find that certain changes to a content object fail to persist. For example, without using limone_zodb we might have just done something more like:

import limone
import persistent

@limone.content_type(Person)
class PersistentPerson(persistent.Persistent):
    pass

jake = PersistentPerson(name='Jake', age=21)

Than, later, in another transaction:

jake.age = 22  # This will persist just fine

While this will work fine if you only change direct attributes of the content object, if you change deep attributes, like friends or phones in the example above, those won't necessarily persist automatically. This is not due to anything particular to Limone. This behavior can be observed with any object containing nested data structures. Let's take a look at this example which doesn't use Limone:

import persistent

class C(persistent.Persistent):
    def __init__(self):
        self.foo = 'Hello'
        self.bars = ['tiki', 'biker']

o = C()

Assuming we have stored the instance o in the ZODB and retrieved it another transaction, we could try to modify it. If we change the value of o.foo and commit our transactoin, a new copy of o will be written to the ZODB and our change will have been saved:

import transaction

o.foo = 'Howdy'  # This change will persist
transaction.commit()

If, on the other hand, we add a new value to o.bars, then this change alone will not be sufficient to cause a new value of o to be written to the ZODB and the change will not be persisted when the transaction is committed:

o.bars.append('lesbian')  # Change does not persist
transaction.commit()

The reason for this lies in how persistent.Persistent object work. persistent.Persistent overrides the __setattr__ method of object so that when an attribute is set on a persistent object, that object is advertised to the transaction as having been changed, and a new copy is written when the transaction is committed. bars, though, is just a regular old Python list that doesn't know anything about persistence. Mutating that list does not advertise the change to the transaction, so the ZODB doesn't know that there is anything new to be written to the database.

For this exact same reason, changes to a Limone content object that only involve a nested data structure, will fail to advertise to the transaction that a change has occurred. If these are the only changes to an object, that object's new state will not be written to the database when the transaction is committed.

Limone ZODB gets around this problem by passing a metaclass into the normal Limone content type generation process which makes sure that making any change to a Limone ZODB content object, at any level of the content structure, will cause the change to be persisted automatically, without any hoops to be jumped through by the developer.

About

Extension of Limone for working with content objects that will be persisted in the ZODB.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages