class ObjectListBase(object): """Mixin class for lists of objects. This mixin class can be added as a base class for an object that is implementing a list of objects. It adds a single field of 'objects', which is the list store, and behaves like a list itself. It supports serialization of the list of objects automatically. """ fields = { 'objects': fields.ListOfObjectsField('VersionedObject'), } # This is a dictionary of my_version:child_version mappings so that # we can support backleveling our contents based on the version # requested of the list object. child_versions = {} def __init__(self, *args, **kwargs): super(ObjectListBase, self).__init__(*args, **kwargs) if 'objects' not in kwargs: self.objects = [] self._changed_fields.discard('objects') def __iter__(self): """List iterator interface.""" return iter(self.objects) def __len__(self): """List length.""" return len(self.objects) def __getitem__(self, index): """List index access.""" if isinstance(index, slice): new_obj = self.__class__() new_obj.objects = self.objects[index] # NOTE(danms): We must be mixed in with a VersionedObject! new_obj.obj_reset_changes() new_obj._context = self._context return new_obj return self.objects[index] def __contains__(self, value): """List membership test.""" return value in self.objects def count(self, value): """List count of value occurrences.""" return self.objects.count(value) def index(self, value): """List index of value.""" return self.objects.index(value) def sort(self, cmp=None, key=None, reverse=False): self.objects.sort(cmp=cmp, key=key, reverse=reverse) def obj_make_compatible(self, primitive, target_version): primitives = primitive['objects'] child_target_version = self.child_versions.get(target_version, '1.0') for index, item in enumerate(self.objects): self.objects[index].obj_make_compatible( primitives[index]['versioned_object.data'], child_target_version) primitives[index][ 'versioned_object.version'] = child_target_version def obj_what_changed(self): changes = set(self._changed_fields) for child in self.objects: if child.obj_what_changed(): changes.add('objects') return changes
class MyObj(base.VersionedPersistentObject, base.VersionedObject, base.VersionedObjectDictCompat): VERSION = '1.6' fields = { 'foo': fields.Field(fields.Integer(), default=1), 'bar': fields.Field(fields.String()), 'missing': fields.Field(fields.String()), 'readonly': fields.Field(fields.Integer(), read_only=True), 'rel_object': fields.ObjectField('MyOwnedObject', nullable=True), 'rel_objects': fields.ListOfObjectsField('MyOwnedObject', nullable=True), } @staticmethod def _from_db_object(context, obj, db_obj): self = MyObj() self.foo = db_obj['foo'] self.bar = db_obj['bar'] self.missing = db_obj['missing'] self.readonly = 1 return self def obj_load_attr(self, attrname): setattr(self, attrname, 'loaded!') @base.remotable_classmethod def query(cls, context): obj = cls(context=context, foo=1, bar='bar') obj.obj_reset_changes() return obj @base.remotable def marco(self, context): return 'polo' @base.remotable def _update_test(self, context): if context.tenant == 'alternate': self.bar = 'alternate-context' else: self.bar = 'updated' @base.remotable def save(self, context): self.obj_reset_changes() @base.remotable def refresh(self, context): self.foo = 321 self.bar = 'refreshed' self.obj_reset_changes() @base.remotable def modify_save_modify(self, context): self.bar = 'meow' self.save() self.foo = 42 self.rel_object = MyOwnedObject(baz=42) def obj_make_compatible(self, primitive, target_version): super(MyObj, self).obj_make_compatible(primitive, target_version) # NOTE(danms): Simulate an older version that had a different # format for the 'bar' attribute if target_version == '1.1' and 'bar' in primitive: primitive['bar'] = 'old%s' % primitive['bar']
class Foo(base.ObjectListBase, base.VersionedObject): fields = {'objects': fields.ListOfObjectsField('Bar')}
class MyList(base.ObjectListBase, base.VersionedObject): fields = {'objects': fields.ListOfObjectsField('MyObj')}