Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
 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)