def dehydrate(self, bundle): assert bundle.obj the_m2ms = None if isinstance(self.attribute, basestring): the_m2ms = getattr(bundle.obj, self.attribute) elif callable(self.attribute): the_m2ms = self.attribute(bundle) if not the_m2ms: if not self.null: raise exceptions.ApiFieldError( "The document %r has an empty attribute '%s' and does not allow a null value." % (bundle.obj, self.attribute)) return [] self.m2m_resources = [] m2m_dehydrated = [] for index, m2m in enumerate(the_m2ms): m2m.pk = index m2m.parent = bundle.obj m2m_resource = self.get_related_resource(m2m) m2m_bundle = tastypie_bundle.Bundle(obj=m2m, request=bundle.request) self.m2m_resources.append(m2m_resource) m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource)) return m2m_dehydrated
def dehydrate(self, bundle, for_list=True): the_m2ms = None if isinstance(self.attribute, basestring): the_m2ms = getattr(bundle.obj, self.attribute) elif callable(self.attribute): the_m2ms = self.attribute(bundle) if not the_m2ms: if not self.null: raise exceptions.ApiFieldError( "The document %r has an empty attribute '%s' and does not allow a null value." % (bundle.obj, self.attribute)) return [] self.m2m_resources = [] m2m_dehydrated = [] # the_m2ms is a list, not a queryset for m2m in the_m2ms: m2m_resource = self.get_related_resource(m2m) m2m_bundle = tastypie_bundle.Bundle(obj=m2m, request=bundle.request) self.m2m_resources.append(m2m_resource) m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource, for_list=for_list)) return m2m_dehydrated
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 dehydrate(self, bundle, for_list=True): if not bundle.obj or not bundle.obj.pk: if not self.null: raise exceptions.ApiFieldError( "The document %r does not have a primary key and can not be used in a ReferencedList context." % bundle.obj) return [] the_m2ms = None if isinstance(self.attribute, basestring): the_m2ms = getattr(bundle.obj, self.attribute) elif callable(self.attribute): the_m2ms = self.attribute(bundle) if not the_m2ms: if not self.null: raise exceptions.ApiFieldError( "The document %r has an empty attribute '%s' and does not allow a null value." % (bundle.obj, self.attribute)) return [] self.m2m_resources = [] m2m_dehydrated = [] # the_m2ms is a list, not a queryset for m2m in the_m2ms: m2m_resource = self.get_related_resource(m2m) m2m_bundle = tastypie_bundle.Bundle(obj=m2m, request=bundle.request) self.m2m_resources.append(m2m_resource) if tastypie.__version__ >= (0, 9, 15): m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource, for_list=for_list)) else: m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource)) return m2m_dehydrated
def build_related_resource(self, value, **kwargs): # A version of build_related_resource which allows only dictionary-like data if hasattr(value, 'items'): self.fk_resource = self.to_class(self.get_api_name()) # We force resource to cannot be updated so that # it is just constructed by resource_from_data self.fk_resource.can_update = lambda: False return self.resource_from_data(self.fk_resource, value, **kwargs) # Or if related object already exists (this happens with PATCH request) elif getattr(value, 'obj', None): return value else: raise exceptions.ApiFieldError( "The '%s' field was not given a dictionary-alike data: %s." % (self.instance_name, value))
def dehydrate(self, bundle, for_list=True): assert bundle.obj the_m2ms = None if isinstance(self.attribute, basestring): the_m2ms = getattr(bundle.obj, self.attribute) elif callable(self.attribute): the_m2ms = self.attribute(bundle) if not the_m2ms: if not self.null: raise exceptions.ApiFieldError( "The document %r has an empty attribute '%s' and does not allow a null value." % (bundle.obj, self.attribute)) return [] self.m2m_resources = [] m2m_dehydrated = [] # the_m2ms is a list, not a queryset for index, m2m in enumerate(the_m2ms): m2m.parent = bundle.obj m2m_resource = self.get_related_resource(m2m) pk_field = getattr(m2m_resource._meta, 'id_field', None) if pk_field is None: m2m.pk = index else: m2m.__class__.pk = link_property(pk_field) m2m_bundle = tastypie_bundle.Bundle(obj=m2m, request=bundle.request) self.m2m_resources.append(m2m_resource) if tastypie.__version__ >= (0, 9, 15): m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource, for_list=for_list)) else: m2m_dehydrated.append( self.dehydrate_related(m2m_bundle, m2m_resource)) return m2m_dehydrated
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)