Source code for oldman.storage.resource

from types import GeneratorType

from rdflib import URIRef, RDF

from oldman.core.exception import OMInternalError
from oldman.core.id import PermanentId
from oldman.core.resource.resource import Resource


[docs]class StoreResource(Resource): """StoreResource: resource manipulated by the data store. End-users should not manipulate it. Is serializable (pickable). :param previous_id: TODO: describe (maybe a temporary one). :param model_manager: :class:`~oldman.model.manager.ModelManager` object. Gives access to its models. :param store: :class:`~oldman.store.store.Store` object. Store that has authority on this resource. :param kwargs: Other parameters considered by the :class:`~oldman.resource.Resource` constructor and values indexed by their attribute names. """ def __init__(self, previous_id, model_manager, store, session, types=None, is_new=True, **kwargs): resource_id = previous_id Resource.__init__(self, resource_id, model_manager, session, types=types, is_new=is_new, **kwargs) self._store = store @classmethod
[docs] def load_from_graph(cls, model_manager, store, session, iri, subgraph, is_new=True): """Loads a new :class:`~oldman.resource.StoreResource` object from a sub-graph. TODO: update the comments. :param model_manager: :class:`~oldman.model.manager.StoreModelManager` object. :param iri: IRI of the resource. :param subgraph: :class:`rdflib.Graph` object containing triples about the resource. :param is_new: When is `True` and `id` given, checks that the IRI is not already existing in the `union_graph`. Defaults to `True`. :return: The :class:`~oldman.resource.store.StoreResource` object created. """ types = list({unicode(t) for t in subgraph.objects(URIRef(iri), RDF.type)}) instance = cls(PermanentId(iri), model_manager, store, session, types=types, is_new=is_new) instance.update_from_graph(subgraph, initial=True) return instance
@property def store(self): return self._store def __getstate__(self): """Pickles this resource.""" state = {name: getattr(self, name) for name in self._pickle_attribute_names} state["store_name"] = self._store.name # Reversed order so that important models can overwrite values reversed_models = self._models reversed_models.reverse() for model in reversed_models: for name, attr in model.om_attributes.iteritems(): value = attr.get_lightly(self) if isinstance(value, GeneratorType): if attr.container == "@list": value = list(value) else: value = set(value) if value is not None: state[name] = value return state def __setstate__(self, state): """Unpickles this resource from its serialized `state`.""" required_fields = self._pickle_attribute_names + ["store_name"] for name in required_fields: if name not in state: #TODO: find a better exception (due to the cache) raise OMInternalError(u"Required field %s is missing in the cached state" % name) self._id = state["_id"] self._is_new = state["_is_new"] self._init_non_persistent_attributes(self._id) # Have to be re-attached self._session = None # Store from oldman.storage.store.store import Store self._store = Store.get_store(state["store_name"]) self._model_manager = self._store.model_manager # Models and types self._models, self._types = self._model_manager.find_models_and_types(state["_types"]) self._former_types = set(self._types) self._tmp_attribute_values = {} # Attributes (Python attributes or OMAttributes) for name, value in state.iteritems(): if name in ["store_name", "_id", "_types", "_is_new"]: continue elif name in self._special_attribute_names: setattr(self, name, value) # OMAttributes else: self._tmp_attribute_values[name] = value
[docs] def reattach(self, xstore_session): if self._session is None: self._session = xstore_session # Affects the attribute values for name, value in self._tmp_attribute_values.iteritems(): attribute = self._get_om_attribute(name) attribute.set(self, value) # Clears former values (allows modification) attribute.receive_storage_ack(self) self._tmp_attribute_values = None else: # TODO: find a better exception raise Exception("Already attached StoreResource %s" % self)
[docs] def prepare_deletion(self): self._former_types = self._types self._types = [] # Removes its attribute for attr in self.attributes: setattr(self, attr.name, None) # def save(self, is_end_user=True): # """Saves it into the `data_store` and its `resource_cache`. # # Raises an :class:`oldman.exception.OMEditError` exception if invalid. # # :param is_end_user: `False` when an authorized user (not a regular end-user) # wants to force some rights. Defaults to `True`. # See :func:`~oldman.attribute.OMAttribute.check_validity` for further details. # :return: The :class:`~oldman.resource.resource.Resource` object itself.""" # # # Checks # attributes = self._extract_attribute_list() # for attr in attributes: # attr.check_validity(self, is_end_user) # # # Find objects to delete # objects_to_delete = [] # for attr in attributes: # if not attr.has_changed(self): # continue # # # Some former objects may be deleted # if attr.om_property.type == OBJECT_PROPERTY: # former_refs, new_refs = attr.diff(self) # # if isinstance(former_refs, dict): # raise NotImplementedError("Object dicts are not yet supported.") # former_refs = former_refs if isinstance(former_refs, (set, list)) else [former_refs] # # # Cache invalidation (because of possible reverse properties) # resources_to_invalidate = set(new_refs) if isinstance(new_refs, (set, list)) else {new_refs} # resources_to_invalidate.update(former_refs) # for r in resources_to_invalidate: # if r is not None: # iri = r.id.iri if isinstance(r, Resource) else r # self._store.resource_cache.remove_resource_from_iri(iri) # # objects_to_delete += self._filter_objects_to_delete(former_refs) # # # Update literal values and receives the definitive id # self.store.save(self, attributes, self._former_types, self._is_new) # # # Delete the objects # for obj in objects_to_delete: # obj.delete() # # # Clears former values # self._former_types = self._types # for attr in attributes: # attr.receive_storage_ack(self) # # return self # # def delete(self): # """Removes the resource from the `data_store` and its `resource_cache`. # # Cascade deletion is done for related resources satisfying the test # :func:`~oldman.resource.resource.should_delete_resource`. # """ # attributes = self._extract_attribute_list() # for attr in attributes: # # Delete blank nodes recursively # if attr.om_property.type == OBJECT_PROPERTY: # value = getattr(self, attr.name) # if value is not None: # objs = value if isinstance(value, (list, set, GeneratorType)) else [value] # for obj in objs: # if should_delete_resource(obj): # self._logger.debug(u"%s deleted with %s" % (obj.id, self._id)) # obj.delete() # else: # self._logger.debug(u"%s not deleted with %s" % (obj.id, self._id)) # # Cache invalidation (because of possible reverse properties) # self._store.resource_cache.remove_resource(obj) # # setattr(self, attr.name, None) # # #Types # self._change_types(set()) # self._store.delete(self, attributes, self._former_types) # # # Clears former values # for attr in attributes: # attr.receive_storage_ack(self) # self._is_new = False # # def _filter_objects_to_delete(self, refs): # return [ref.get() for ref in refs # if ref is not None and is_blank_node(ref.object_iri)]