def obj_create(self, bundle, **kwargs): # Feed request object with the bundle request = bundle.request # Since we are not using the native save method # we need to check autorization here self.authorized_create_detail(self.get_object_list(request), bundle) # The only field allowed during creation is "name" data = dict(name=bundle.data.get("name", None), _author=[request.user.id]) data = self.validate(data) # Model class model = self.get_model() # Find the node associate to this model model_node = self.get_model_node() # Start a transaction to batch insert values with connection.transaction(commit=False) as tx: # Create a brand new node node = connection.nodes.create(**data) # Instanciate its type rel_type = connection.relationships.create(model_node, "<<INSTANCE>>", node) # Commit the transaction tx.commit() # Create an object to build the bundle obj = node.properties obj["id"] = node.id # update the cache topic_cache.incr_version(request.current_topic) # Return a new bundle return self.build_bundle(obj=model._neo4j_instance(node), data=obj, request=request)
def get_patch(self, request, **kwargs): pk = kwargs["pk"] # This should be a POST request self.method_check(request, allowed=['post']) self.throttle_check(request) # User must be authentication self.is_authenticated(request) bundle = self.build_bundle(request=request) # User allowed to update this model self.authorized_update_detail(self.get_object_list(bundle.request), bundle) # Get the node's data using the rest API try: node = connection.nodes.get(pk) # Node not found except client.NotFoundError: raise Http404("Not found.") # Load every relationship only when we need to update a relationship node_rels = None # Parse only body string body = json.loads(request.body) if type(request.body) is str else request.body # Copy data to allow dictionary resizing data = body.copy() # Received per-field sources if "field_sources" in data: # field_sources must not be treated here, see patch_source method field_sources = data.pop("field_sources") # Validate data. # If it fails, it will raise a ValidationError data = self.validate(data) # Get author list (or a new array if ) author_list = node.properties.get("_author", []) # This is the first time the current user edit this node if int(request.user.id) not in author_list: # Add the author to the author list data["_author"] = author_list + [request.user.id] # @TODO check that 'node' is an instance of 'model' # Set new values to the node for field_name in data: field = self.get_model_field(field_name) field_value = data[field_name] # The value can be a list of ID for relationship if field.get_internal_type() is 'Relationship': # Pluck id from the list field_ids = [ value for value in field_value if value is not int(pk) ] # Prefetch all relationship if node_rels is None: node_rels = node.relationships.all() # Get relationship name rel_type = self.get_model_field(field_name)._type # We don't want to add this relation twice so we extract # every node connected to the current one through this type # of relationship. "existing_rels_id" will contain the ids of # every node related to this one. existing_rels = [ rel for rel in node_rels if rel.type == rel_type ] existing_rels_id = [ graph.opposite(rel, pk) for rel in existing_rels ] # Get every ids from "field_ids" that ain't not in # the list of existing relationship "existing_rel_id". new_rels_id = set(field_ids).difference(existing_rels_id) # Get every ids from "existing_rels_id" that ain't no more # in the new list of relationships "field_ids". old_rels_id = set(existing_rels_id).difference(field_ids) # Start a transaction to batch import values with connection.transaction(commit=False) as tx: # Convert ids or related node to *node* instances new_rels_node = [ connection.nodes.get(idx) for idx in new_rels_id ] # Convert ids or unrelated node to *relationships* instances old_rels = [] # Convert ids list into relationship instances for idx in old_rels_id: # Find the relationship that match with this id matches = [ rel for rel in existing_rels if graph.connected(rel, idx) ] # Merge the list of relationships old_rels = old_rels + matches # Commit change when every field was treated tx.commit() # Start a transaction to batch insert/delete values with connection.transaction(commit=False) as tx: # Then create the new relationships (using nodes instances) # Outcoming relationship if field.direction == 'out': [ connection.relationships.create(node, rel_type, n) for n in new_rels_node ] # Incoming relationship elif field.direction == 'in': [ connection.relationships.create(n, rel_type, node) for n in new_rels_node ] # Then delete the old relationships (using relationships instance) [ rel.delete() for rel in old_rels ] # Commit change when every field was treated tx.commit() # Or a literal value # (integer, date, url, email, etc) else: # Current model model = self.get_model() # Fields fields = { x['name'] : x for x in iterate_model_fields(model) } # Remove the values if field_value in [None, '']: if field_name == 'image' and fields[field_name]['type'] == 'URLField': self.remove_node_file(node, field_name, True) # The field may not exists (yet) try: node.delete(field_name) # It's OK, it just means we don't have to remove it except client.NotFoundError: pass # We simply update the node property # (the value is already validated) else: if field_name in fields: if 'is_rich' in fields[field_name]['rules'] and fields[field_name]['rules']['is_rich']: data[field_name] = field_value = bleach.clean(field_value, tags=("br", "blockquote", "ul", "ol", "li", "b", "i", "u", "a", "p"), attributes={ '*': ("class",), 'a': ("href", "target") }) if field_name == 'image' and fields[field_name]['type'] == 'URLField': self.remove_node_file(node, field_name, True) try: image_file = download_url(data[field_name]) path = default_storage.save(os.path.join(settings.UPLOAD_ROOT, image_file.name) , image_file) data[field_name] = field_value = path.replace(settings.MEDIA_ROOT, "") except UnavailableImage: data[field_name] = field_value = "" except NotAnImage: data[field_name] = field_value = "" except OversizedFile: data[field_name] = field_value = "" node.set(field_name, field_value) # update the cache topic_cache.incr_version(request.current_topic) # And returns cleaned data return self.create_response(request, data)