def test_changes(): a = {'a': 12, 'b': '34'} b = {'a': 1, 'b': '34'} changes = get_changes(a, b) assert changes == {'a': 1} with pytest.raises(KeyError): get_changes(a, {})
def _get_changes(self, persistable): changes = {} existing = None registry = self.type_registry if isinstance(persistable, PersistableType): if issubclass(persistable, Relationship): # not stored in the db; must be static return None, {} query = """ MATCH {} OPTIONAL MATCH (attr)-[:DECLAREDON*0..]->(cls) RETURN cls, collect(attr.name) """.format(get_match_clause(persistable, 'cls', registry)) # don't use self.query since we don't want to convert the py2neo # node into an object rows = self._execute(query) cls_node, attrs = next(rows, (None, None)) if cls_node is None: # have not found the cls return None, {} existing_cls_attrs = cls_node._properties # Make sure we get a clean view of current data. registry.refresh_type(persistable) new_cls_attrs = registry.object_to_dict(persistable) # If any existing keys in "new" are missing in "old", add `None`s. # Unlike instance attributes, we just need to remove the properties # from the node, which we can achieve by setting the values to None for key in set(existing_cls_attrs) - set(new_cls_attrs): new_cls_attrs[key] = None changes = get_changes(old=existing_cls_attrs, new=new_cls_attrs) attrs = set(attrs) modified_attrs = {} descr = registry.get_descriptor(persistable) for name, attr in descr.declared_attributes.items(): if name not in attrs: modified_attrs[name] = attr del_attrs = set(attrs) for name in Descriptor(persistable).attributes.keys(): del_attrs.discard(name) for name in del_attrs: modified_attrs[name] = None if modified_attrs: changes['attributes'] = modified_attrs # we want to return the existing class type_id = get_type_id(persistable) existing = registry.get_descriptor_by_id(type_id).cls else: try: query = 'MATCH {} RETURN obj'.format( get_match_clause(persistable, 'obj', registry) ) except NoUniqueAttributeError: existing = None else: existing = self.query_single(query) if existing is not None: existing_props = registry.object_to_dict(existing) props = registry.object_to_dict(persistable) if existing_props == props: return existing, {} changes = get_changes(old=existing_props, new=props) return existing, changes
def _get_changes(self, persistable): changes = {} existing = None obj_type = type(persistable) registry = self.type_registry if isinstance(persistable, PersistableType): # this is a class, we need to get it and it's attrs idx_name = get_index_name(PersistableType) self._conn.get_or_create_index(neo4j.Node, idx_name) type_id = get_type_id(persistable) query_args = { 'type_id': type_id } query = join_lines( 'START cls=node:%s(id={type_id})' % idx_name, 'MATCH attr -[:DECLAREDON*0..]-> cls', 'RETURN cls, collect(attr.name?)' ) # don't use self.query since we don't want to convert the py2neo # node into an object rows = self._execute(query, **query_args) cls_node, attrs = next(rows, (None, None)) if cls_node is None: # have not found the cls return None, {} existing_cls_attrs = cls_node.get_properties() new_cls_attrs = registry.object_to_dict(persistable) # If any existing keys in "new" are missing in "old", add `None`s. # Unlike instance attributes, we just need to remove the properties # from the node, which we can achieve by setting the values to None for key in set(existing_cls_attrs) - set(new_cls_attrs): new_cls_attrs[key] = None changes = get_changes(old=existing_cls_attrs, new=new_cls_attrs) attrs = set(attrs) modified_attrs = {} descr = registry.get_descriptor(persistable) for name, attr in descr.declared_attributes.items(): if name not in attrs: modified_attrs[name] = attr del_attrs = set(attrs) for name in Descriptor(persistable).attributes.keys(): del_attrs.discard(name) for name in del_attrs: modified_attrs[name] = None if modified_attrs: changes['attributes'] = modified_attrs # we want to return the existing class existing = registry.get_descriptor_by_id(type_id).cls else: existing = self.get(obj_type, **get_attr_filter(persistable, registry)) if existing is not None: existing_props = registry.object_to_dict(existing) props = registry.object_to_dict(persistable) if isinstance(persistable, Relationship): # if the relationship has endoints, also consider # whether those have changed for rel_attr in ['start', 'end']: new = getattr(persistable, rel_attr, None) if new is None: continue ex_rel_attr = getattr(existing, rel_attr) ex_rel_identifier = get_attr_filter(ex_rel_attr, registry) if new != ex_rel_identifier: props[rel_attr] = new if existing_props == props: return existing, {} changes = get_changes(old=existing_props, new=props) return existing, changes