def obj_delete(self, bundle, **kwargs): obj = kwargs.pop('_obj', None) if not getattr(obj, 'pk', None): try: obj = self.obj_get(bundle=bundle, **kwargs) except (queryset.DoesNotExist, exceptions.ObjectDoesNotExist): raise tastypie_exceptions.NotFound( "A document instance matching the provided arguments could not be found." ) object_list = getattr(self.instance, self.attribute) pk_field = getattr(self._meta, 'id_field', None) if pk_field is None: object_list.pop(obj.pk) else: object_list.pop( self.find_embedded_document(object_list, pk_field, obj.pk)) # Make sure to delete FileField files for fieldname, field in obj._fields.items(): if isinstance(field, mongoengine_fields.FileField): obj[fieldname].delete() self.instance.save()
def obj_update(self, bundle, skip_errors=False, **kwargs): try: if not bundle.obj or not getattr(bundle.obj, 'pk', None): try: bundle.obj = self.obj_get(bundle=bundle, **kwargs) except (queryset.DoesNotExist, exceptions.ObjectDoesNotExist): raise tastypie_exceptions.NotFound( "A document instance matching the provided arguments could not be found." ) bundle = self.full_hydrate(bundle) object_list = getattr(self.instance, self.attribute) pk_field = getattr(self._meta, 'id_field', None) if pk_field is None: object_list[bundle.obj.pk] = bundle.obj else: object_list[self.find_embedded_document( object_list, pk_field, bundle.obj.pk)] = bundle.obj self.save_related(bundle) self.instance.save() m2m_bundle = self.hydrate_m2m(bundle) self.save_m2m(m2m_bundle) return bundle except mongoengine.ValidationError as ex: raise exceptions.ValidationError(ex.message)
def full_hydrate(self, bundle): # When updating objects, we want to force only updates of the same type, and object # should be completely replaced if type is changed, so we throw and exception here # to direct program logic flow (it is cached and replace instead of update is tried) if bundle.obj and self._meta.object_class is not bundle.obj.__class__: raise tastypie_exceptions.NotFound( "A document instance matching the provided arguments could not be found." ) bundle = super(MongoEngineResource, self).full_hydrate(bundle) # We redo check for required fields as Tastypie is not # reliable as it does checks in an inconsistent way # (https://github.com/toastdriven/django-tastypie/issues/491) for field_object in self.fields.itervalues(): if field_object.readonly or getattr(field_object, '_primary_key', False): continue if not field_object.attribute: continue # Tastypie also skips setting value if it is None, but this means # updates to None are ignored: this is not good as it hides invalid # PUT/PATCH REST requests (setting value to None which should fail # validation (field required) is simply ignored and value is left # as it is) # (https://github.com/toastdriven/django-tastypie/issues/492) # We hydrate field again only if existing value is not None if getattr(bundle.obj, field_object.attribute, None) is not None: value = NOT_HYDRATED # Tastypie also ignores missing fields in PUT, # so we check for missing field here # (https://github.com/toastdriven/django-tastypie/issues/496) if field_object.instance_name not in bundle.data: if field_object.has_default(): if callable(field_object.default): value = field_object.default() else: value = field_object.default else: value = None else: value = field_object.hydrate(bundle) if value is None: setattr(bundle.obj, field_object.attribute, None) if field_object.blank or field_object.null: continue # We are just trying to fix Tastypie here, for other "null" values # like [] and {} we leave to MongoEngine validate to catch them if getattr(bundle.obj, field_object.attribute, None) is None: raise tastypie_exceptions.ApiFieldError( "The '%s' field has no data and doesn't allow a default or null value." % field_object.instance_name) return bundle
def obj_delete(self, bundle, store, **kwargs): with git_checkpoint(store, "Completing Task"): try: store.log_message("Task %s completed.", kwargs['pk']) store.client.task_done(uuid=kwargs['pk']) return bundle except ValueError: raise exceptions.NotFound()
def obj_delete(self, bundle, **kwargs): self._reset_collection() # MongoEngine exceptions are separate from Django exceptions and Tastypie # expects Django exceptions, so we catch it here ourselves and raise NotFound try: return super(MongoEngineResource, self).obj_delete(bundle, **kwargs) except queryset.DoesNotExist: raise tastypie_exceptions.NotFound("A document instance matching the provided arguments could not be found.")
def stop_task(self, request, uuid, store, **kwargs): if not request.method == 'POST': return HttpResponseNotAllowed(request.method) try: store.client.task_stop(uuid=uuid) except ValueError: raise exceptions.NotFound() store.log_message("Task %s stopped.", uuid) return HttpResponse( status=200 )
def obj_update(self, bundle, skip_errors=False, **kwargs): self._reset_collection() if not bundle.obj or not getattr(bundle.obj, 'pk', None): try: bundle.obj = self.obj_get(bundle=bundle, **kwargs) except (queryset.DoesNotExist, exceptions.ObjectDoesNotExist): raise tastypie_exceptions.NotFound("A document instance matching the provided arguments could not be found.") self.authorized_update_detail(self.get_object_list(bundle.request), bundle) bundle = self.full_hydrate(bundle) return self.save(bundle, skip_errors=skip_errors)
def stop_task(self, request, uuid, store, **kwargs): if not request.method == 'POST': return HttpResponseNotAllowed(request.method) try: store.client.task_stop(uuid=uuid) except ValueError: raise exceptions.NotFound() store.log_message("Task %s stopped.", uuid) return HttpResponse( json.dumps({ 'message': 'OK', }), content_type='application/json', )
def obj_get(self, bundle, store, **kwargs): task = store.client.get_task(uuid=kwargs['pk'])[1] if not task: repository_head = store.repository.head() logger.warning( 'Unable to find task with ID %s in repository %s at %s', kwargs['pk'], store.local_path, repository_head, extra={ 'data': { 'store': store, 'local_path': store.local_path, 'pk': kwargs['pk'], 'head': repository_head } }) raise exceptions.NotFound() return Task(task, store=store)
def obj_update(self, bundle, request=None, **kwargs): self._reset_collection() if not bundle.obj or not getattr(bundle.obj, 'pk', None): try: bundle.obj = self.obj_get(request, **kwargs) except (queryset.DoesNotExist, exceptions.ObjectDoesNotExist): raise tastypie_exceptions.NotFound( "A document instance matching the provided arguments could not be found." ) bundle = self.full_hydrate(bundle) self.save_related(bundle) bundle.obj.save() m2m_bundle = self.hydrate_m2m(bundle) self.save_m2m(m2m_bundle) return bundle
def obj_get(self, bundle, **kwargs): try: stream = datastream.Stream(datastream.get_tags(kwargs['pk'])) except datastream_exceptions.StreamNotFound: raise exceptions.NotFound("Stream '%s' not found." % kwargs['pk']) params = self._get_query_params(bundle.request, stream) stream.datapoints = datastream.get_data( stream_id=stream.id, granularity=params['granularity'], start=params['start'], end=params['end'], start_exclusive=params['start_exclusive'], end_exclusive=params['end_exclusive'], reverse=params['reverse'], value_downsamplers=params['value_downsamplers'], time_downsamplers=params['time_downsamplers'], ) return stream
def obj_update(self, bundle, store, **kwargs): with git_checkpoint(store, "Updating Task", sync=True): if bundle.data['uuid'] != kwargs['pk']: raise exceptions.BadRequest( "Changing the UUID of an existing task is not possible.") elif not bundle.data['description']: raise exceptions.BadRequest( "You must specify a description for each task.") original = store.client.get_task(uuid=kwargs['pk'])[1] if not original: raise exceptions.NotFound() bundle.obj = self.get_empty_task(bundle.request) bundle = self.full_hydrate(bundle) data = bundle.obj.get_json() for k in json.loads(bundle.request.body).keys(): v = data.get(k) if ((k in original.FIELDS and original.FIELDS[k].read_only) or k in self.SYNTHETIC_FIELDS): continue original[k] = v changes = original.get_changes(keep=True) store.client.task_update(original) store.log_message( "Task %s updated: %s.", kwargs['pk'], changes, ) bundle.obj = Task( store.client.get_task(uuid=kwargs['pk'])[1], store=store, ) return bundle
def obj_get(self, bundle, store, **kwargs): try: return Task(store.client.get_task(uuid=kwargs['pk'])[1]) except ValueError: raise exceptions.NotFound()
def full_hydrate(self, bundle): # When updating objects, we want to force only updates of the same type, and object # should be completely replaced if type is changed, so we throw and exception here # to direct program logic flow (it is cached and replace instead of update is tried) if bundle.obj and self._meta.object_class is not bundle.obj.__class__: raise tastypie_exceptions.NotFound( "A document instance matching the provided arguments could not be found." ) bundle = super(MongoEngineResource, self).full_hydrate(bundle) # We redo check for required fields as Tastypie is not # reliable as it does checks in an inconsistent way # (https://github.com/toastdriven/django-tastypie/issues/491) for field_object in self.fields.itervalues(): if field_object.readonly: continue if not field_object.attribute: continue value = NOT_HYDRATED # Tastypie also skips setting value if it is None, but this means # updates to None are ignored: this is not good as it hides invalid # PUT/PATCH REST requests (setting value to None which should fail # validation (field required) is simply ignored and value is left # as it is) # (https://github.com/toastdriven/django-tastypie/issues/492) # We hydrate field again only if existing value is not None if getattr(bundle.obj, field_object.attribute, None) is not None: # Tastypie also ignores missing fields in PUT, # so we check for missing field here # (https://github.com/toastdriven/django-tastypie/issues/496) if field_object.instance_name not in bundle.data: if field_object._default is not tastypie_fields.NOT_PROVIDED: if callable(field_object.default): value = field_object.default() else: value = field_object.default else: value = None else: value = field_object.hydrate(bundle) if value is None: # This does not really set None in a way that calling # getattr on bundle.obj would return None later on # This is how MongoEngine is implemented # (https://github.com/hmarr/mongoengine/issues/505) setattr(bundle.obj, field_object.attribute, None) if field_object.blank or field_object.null: continue # We are just trying to fix Tastypie here, for other "null" values # like [] and {} we leave to validate bellow to catch them if getattr( bundle.obj, field_object.attribute, None ) is None or value is None: # We also have to check value, read comment above raise tastypie_exceptions.ApiFieldError( "The '%s' field has no data and doesn't allow a default or null value." % field_object.instance_name) # We validate MongoEngine object here so that possible exception # is thrown before going to MongoEngine layer, wrapped in # Django exception so that it is handled properly # is_valid method is too early as bundle.obj is not yet ready then try: # Validation fails for unsaved related resources, so # we fake pk here temporary, for validation code to # assume resource is saved pk = getattr(bundle.obj, 'pk', None) try: if pk is None: bundle.obj.pk = bson.ObjectId() bundle.obj.validate() finally: if pk is None: bundle.obj.pk = pk except mongoengine.ValidationError, e: raise exceptions.ValidationError(e.message)