示例#1
0
 def alter_detail_data_to_serialize(self, request, bundle, nested=False):
     model = self.get_model()
     # Get relationships fields
     fields = [ f for f in model._meta.fields if f.get_internal_type() == 'Relationship']
     node_rels = bundle.obj.node.relationships.all()
     # If the nested parameter is True, this set
     node_to_retreive = set()
     # Resolve relationships manualy
     for field in fields:
         # Get relationships for this fields
         field_rels = [ rel for rel in node_rels[:] if rel.type == field._type]
         # Filter relationships to keep only the well oriented relationships
         # get the related field informations
         related_field = [f for f in iterate_model_fields(model) if "rel_type" in f and f["rel_type"] == field._type and "name" in f and f["name"] == field._BoundRelationship__attname]
         if related_field:
             # Note (edouard): check some assertions in case I forgot something
             assert len(related_field) == 1, related_field
             assert related_field[0]["direction"]
             # choose the end point to check
             end_point_side = "start" if related_field[0]["direction"] == "out" else "end"
             # filter the relationship
             field_rels = [rel for rel in field_rels if getattr(rel, end_point_side).id == bundle.obj.id]
         # Get node ids for those relationships
         field_oposites = [ graph.opposite(rel, bundle.obj.id) for rel in field_rels ]
         # Save the list into properities
         bundle.data[field.name] = field_oposites
         # Nested mode to true: we need to retreive every node
         if nested: node_to_retreive = set(list(node_to_retreive) + field_oposites)
     # There is node to extract for the graph
     if len(node_to_retreive):
         # Build the query to get all node in one request
         query = "start n=node(%s) RETURN ID(n), n" % ",".join(map(str, node_to_retreive))
         # Get all nodes as raw values to avoid unintended request to the graph
         nodes = connection.query(query, returns=(int, dict))
         # Helper lambda to retreive a node
         retreive_node = lambda idx: next(n[1]["data"] for n in nodes if n[0] == idx)
         # Populate the relationships field with there node instance
         for field in fields:
             # Retreive the list of ids
             for i, idx in enumerate(bundle.data[field.name]):
                 rel_node = retreive_node(idx)
                 # Save the id which is not a node property
                 rel_node["id"] = idx
                 # Update value
                 bundle.data[field.name][i] = self.validate(rel_node, field.target_model, allow_missing=True)
     # Show additional field following the model's rules
     rules = request.current_topic.get_rules().model(self.get_model()).all()
     # All additional relationships
     for key in rules:
         # Filter rules to keep only Neomatch instance.
         # Neomatch is a class to create programmaticly a search related to
         # this node.
         if isinstance(rules[key], Neomatch):
             bundle.data[key] = rules[key].query(bundle.obj.id)
     return bundle
示例#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)