docs: def save(self):
          """
          save data into DB:
  
           1. save new (missing) data
           2. update only when needed
           3. delete old data
           4. generate report that will be printed
  
          constraints:
           * ensure new nodes do not take a name/slug which is already used
           * validate through django before saving
           * use good defaults
          """
          self.key_mapping()
          # retrieve all items
          items = self.parsed_data
  
          # init empty lists
          added_nodes = []
          changed_nodes = []
          unmodified_nodes = []
  
          # retrieve a list of all the slugs of this layer
          layer_nodes_slug_list = Node.objects.filter(layer=self.layer).values_list('slug', flat=True)
          # keep a list of all the nodes of other layers
          other_layers_slug_list = Node.objects.exclude(layer=self.layer).values_list('slug', flat=True)
          # init empty list of slug of external nodes that will be needed to perform delete operations
          processed_slug_list = []
          deleted_nodes_count = 0
  
          # loop over every item
          for item in items:
  
              item = self._convert_item(item)
  
              number = 1
              original_name = item['name']
              needed_different_name = False
  
              while True:
                  # items might have the same name... so we add a number..
                  if item['slug'] in processed_slug_list or item['slug'] in other_layers_slug_list:
                      needed_different_name = True
                      number = number + 1
                      item['name'] = "%s - %d" % (original_name, number)
                      item['slug'] = slugify(item['name'])
                  else:
                      if needed_different_name:
                          self.verbose('needed a different name for %s, trying "%s"' % (original_name, item['name']))
                      break
  
              # default values
              added = False
              changed = False
  
              try:
                  # edit existing node
                  node = Node.objects.get(slug=item['slug'], layer=self.layer)
              except Node.DoesNotExist:
                  # add a new node
                  node = Node()
                  node.layer = self.layer
                  added = True
  
              # loop over fields and store data only if necessary
              for field in Node._meta.fields:
                  # geometry is a special case, skip
                  if field.name == 'geometry':
                      continue
                  # skip if field is not present in values
                  if field.name not in item.keys():
                      continue
                  # shortcut for value
                  value = item[field.name]
                  # if value is different than what we have
                  if value is not None and getattr(node, field.name) != value:
                      # set value
                      setattr(node, field.name, value)
                      # indicates that a DB query is necessary
                      changed = True
  
              if added or (node.geometry.equals(item['geometry']) is False
                           and node.geometry.equals_exact(item['geometry']) is False):
                  node.geometry = item['geometry']
                  changed = True
  
              node.data = node.data or {}
  
              # store any additional key/value in HStore data field
              for key, value in item['data'].items():
                  if node.data[key] != value:
                      node.data[key] = value
                      changed = True
  
              # perform save or update only if necessary
              if added or changed:
                  try:
                      node.full_clean()
                      if None not in [node.added, node.updated]:
                          node.save(auto_update=False)
                      else:
                          node.save()
                  except Exception as e:
                      raise Exception('error while processing "%s": %s' % (node.name, e))
  
              if added:
                  added_nodes.append(node)
                  self.verbose('new node saved with name "%s"' % node.name)
              elif changed:
                  changed_nodes.append(node)
                  self.verbose('node "%s" updated' % node.name)
              else:
                  unmodified_nodes.append(node)
                  self.verbose('node "%s" unmodified' % node.name)
  
              # fill node list container
              processed_slug_list.append(node.slug)
  
          # delete old nodes
          for local_node in layer_nodes_slug_list:
              # if local node not found in external nodes
              if local_node not in processed_slug_list:
                  # retrieve from DB and delete
                  node = Node.objects.get(slug=local_node)
                  # store node name to print it later
                  node_name = node.name
                  node.delete()
                  # then increment count that will be included in message
                  deleted_nodes_count = deleted_nodes_count + 1
                  self.verbose('node "%s" deleted' % node_name)
  
          # message that will be returned
          self.message = """
              %s nodes added
              %s nodes changed
              %s nodes deleted
              %s nodes unmodified
              %s total external records processed
              %s total local nodes for this layer
          """ % (
              len(added_nodes),
              len(changed_nodes),
              deleted_nodes_count,
              len(unmodified_nodes),
              len(items),
              Node.objects.filter(layer=self.layer).count()
          )