def deserialize_tags(self, tags):
          if isinstance(tags, basestring):
              return tags.split(',') if tags else []
 -        return tags
 +        return tags or []
  
      def serialize_depends(self, cur_dependencies):
          # Return the list of uuids
      read_only_fields = []
  
      def _load_data(self, data):
 -        self._data = data
 +        self._data = dict((key, self._deserialize(key, value))
 +                          for key, value in data.items())
          # We need to use a copy for original data, so that changes
 -        # are not propagated. Shallow copy is alright, since data dict uses only
 -        # primitive data types
 -        self._original_data = data.copy()
 -
 -    def _update_data(self, data, update_original=False):
 -        """
 -        Low level update of the internal _data dict. Data which are coming as
 -        updates should already be serialized. If update_original is True, the
 -        original_data dict is updated as well.
 -        """
 -
 -        self._data.update(data)
 -
 -        if update_original:
 -            self._original_data.update(data)
 +        # are not propagated.
 +        self._original_data = copy.deepcopy(self._data)
  
      def __getitem__(self, key):
          # This is a workaround to make TaskResource non-iterable
          except ValueError:
              pass
  
 -        return self._deserialize(key, self._data.get(key))
 +        if key not in self._data:
 +            self._data[key] = self._deserialize(key, None)
 +
 +        return self._data.get(key)
  
      def __setitem__(self, key, value):
          if key in self.read_only_fields:
              raise RuntimeError('Field \'%s\' is read-only' % key)
 -        self._data[key] = self._serialize(key, value)
 +        self._data[key] = value
  
      def __str__(self):
          s = six.text_type(self.__unicode__())
              if self._data.get(key) != self._original_data.get(key):
                  yield key
  
 +    @property
 +    def _is_modified(self):
 +        return bool(list(self._modified_fields))
 +
      @property
      def completed(self):
          return self['status'] == six.text_type('completed')
          # to keep a list of all depedencies in the _data dictionary,
          # not just currently added/removed ones
  
 -        old_dependencies_raw = self._original_data.get('depends','')
 -        old_dependencies = self.deserialize_depends(old_dependencies_raw)
 +        old_dependencies = self._original_data.get('depends', set())
  
          added = self['depends'] - old_dependencies
          removed = old_dependencies - self['depends']
              raise Task.NotSaved("Task needs to be saved before it can be deleted")
  
          # Refresh the status, and raise exception if the task is deleted
 -        self.refresh(only_fields=['status'])
 +        self.refresh()
  
          if self.deleted:
              raise Task.DeletedTask("Task was already deleted")
          self.warrior.execute_command([self['uuid'], 'delete'])
  
          # Refresh the status again, so that we have updated info stored
 -        self.refresh(only_fields=['status'])
 +        self.refresh()
  
  
      def done(self):
              raise Task.NotSaved("Task needs to be saved before it can be completed")
  
          # Refresh, and raise exception if task is already completed/deleted
 -        self.refresh(only_fields=['status'])
 +        self.refresh()
  
          if self.completed:
              raise Task.CompletedTask("Cannot complete a completed task")
          self.warrior.execute_command([self['uuid'], 'done'])
  
          # Refresh the status again, so that we have updated info stored
 -        self.refresh(only_fields=['status'])
 +        self.refresh()
  
      def save(self):
 +        if self.saved and not self._is_modified:
 +            return
 +
          args = [self['uuid'], 'modify'] if self.saved else ['add']
          args.extend(self._get_modified_fields_as_args())
          output = self.warrior.execute_command(args)
  
          args = [self['uuid'], 'annotate', annotation]
          self.warrior.execute_command(args)
 -        self.refresh(only_fields=['annotations'])
 +        self.refresh()
  
      def remove_annotation(self, annotation):
          if not self.saved:
              annotation = annotation['description']
          args = [self['uuid'], 'denotate', annotation]
          self.warrior.execute_command(args)
 -        self.refresh(only_fields=['annotations'])
 +        self.refresh()
  
      def _get_modified_fields_as_args(self):
          args = []
  
          def add_field(field):
              # Add the output of format_field method to args list (defaults to
 -            # field:'value')
 -            format_default = lambda k: "{0}:{1}".format(k,
 -                                           "'{0}'".format(self._data[k])
 -                                           if self._data[k] is not None
 -                                           else '')
 +            # field:value)
 +            serialized_value = self._serialize(field, self._data[field]) or ''
-             format_default = lambda: "{0}:'{1}'".format(field, serialized_value)
++            format_default = lambda: "{0}:{1}".format(
++                field,
++                "'{0}'".format(serialized_value) if serialized_value else ''
++            )
              format_func = getattr(self, 'format_{0}'.format(field),
 -                                  lambda: format_default(field))
 +                                  format_default)
              args.append(format_func())
  
          # If we're modifying saved task, simply pass on all modified fields
  
          return args
  
 -    def refresh(self, only_fields=[]):
 +    def refresh(self):
          # Raise error when trying to refresh a task that has not been saved
          if not self.saved:
              raise Task.NotSaved("Task needs to be saved to be refreshed")
          # with using UUID only.
          args = [self['uuid'] or self['id'], 'export']
          new_data = json.loads(self.warrior.execute_command(args)[0])
 -        if only_fields:
 -            to_update = dict(
 -                [(k, new_data.get(k)) for k in only_fields])
 -            self._update_data(to_update, update_original=True)
 -        else:
 -            self._load_data(new_data)
 +        self._load_data(new_data)
  
  
  class TaskFilter(SerializingObject):