Ejemplo n.º 1
0
    def resource_from_uri(self,
                          fk_resource,
                          uri,
                          request=None,
                          related_obj=None,
                          related_name=None):
        """
        Given a URI is provided, the related resource is attempted to be
        loaded based on the identifiers in the URI.
        """
        err_msg = "Could not find the provided %s object via resource URI '%s'." % (
            fk_resource._meta.resource_name,
            uri,
        )

        if not uri:
            raise ApiFieldError(err_msg)

        try:
            obj = fk_resource.get_via_uri(uri, request=request)
            bundle = fk_resource.build_bundle(obj=obj,
                                              request=request,
                                              via_uri=True)
            return fk_resource.full_dehydrate(bundle)
        except ObjectDoesNotExist:
            raise ApiFieldError(err_msg)
Ejemplo n.º 2
0
    def dehydrate(self, bundle, for_list=True):
        foreign_obj = None

        if callable(self.attribute):
            previous_obj = bundle.obj
            foreign_obj = self.attribute(bundle)
        elif isinstance(self.attribute, six.string_types):
            foreign_obj = bundle.obj

            for attr in self._attrs:
                previous_obj = foreign_obj
                try:
                    foreign_obj = getattr(foreign_obj, attr, None)
                except ObjectDoesNotExist:
                    foreign_obj = None

        if not foreign_obj:
            if not self.null:
                if callable(self.attribute):
                    raise ApiFieldError(
                        "The related resource for resource %s could not be found."
                        % (previous_obj))
                else:
                    raise ApiFieldError(
                        "The model '%r' has an empty attribute '%s' and doesn't allow a null value."
                        % (previous_obj, attr))
            return None

        fk_resource = self.get_related_resource(foreign_obj)
        fk_bundle = Bundle(obj=foreign_obj, request=bundle.request)
        return self.dehydrate_related(fk_bundle,
                                      fk_resource,
                                      for_list=for_list)
Ejemplo n.º 3
0
    def hydrate_m2m(self, bundle):
        if self.readonly:
            return None

        if bundle.data.get(self.instance_name) is None:
            if self.blank:
                return []
            if self.null:
                return []
            raise ApiFieldError(
                "The '%s' field has no data and doesn't allow a null value." %
                self.instance_name)

        kwargs = {
            'request': bundle.request,
        }

        if self.related_name:
            kwargs['related_obj'] = bundle.obj
            kwargs['related_name'] = self.related_name

        return [
            self.build_related_resource(value, **kwargs)
            for value in bundle.data.get(self.instance_name)
            if value is not None
        ]
Ejemplo n.º 4
0
 def to_time(self, s):
     try:
         dt = parse(s)
     except (ValueError, TypeError) as e:
         raise ApiFieldError(str(e))
     else:
         return datetime.time(dt.hour, dt.minute, dt.second, dt.microsecond)
Ejemplo n.º 5
0
    def dehydrate(self, bundle, for_list=True):
        if not bundle.obj or not bundle.obj.pk:
            if not self.null:
                raise ApiFieldError(
                    "The model '%r' does not have a primary key and can not be used in a ToMany context."
                    % bundle.obj)

            return []

        the_m2ms = None
        previous_obj = bundle.obj
        attr = self.attribute

        if callable(self.attribute):
            the_m2ms = self.attribute(bundle)
        elif isinstance(self.attribute, six.string_types):
            the_m2ms = bundle.obj

            for attr in self._attrs:
                previous_obj = the_m2ms
                try:
                    the_m2ms = getattr(the_m2ms, attr, None)
                except ObjectDoesNotExist:
                    the_m2ms = None

                if the_m2ms is None:
                    break

        if the_m2ms is None:
            if not self.null:
                raise ApiFieldError(
                    "The model '%r' has an empty attribute '%s' and doesn't allow a null value."
                    % (previous_obj, attr))

        if isinstance(the_m2ms, models.Manager):
            the_m2ms = the_m2ms.all()

        m2m_dehydrated = [
            self.dehydrate_related(Bundle(obj=m2m, request=bundle.request),
                                   self.get_related_resource(m2m),
                                   for_list=for_list) for m2m in the_m2ms
        ]

        return m2m_dehydrated
Ejemplo n.º 6
0
    def hydrate(self, bundle):
        value = super(DateTimeField, self).hydrate(bundle)

        if value and not hasattr(value, 'year'):
            if isinstance(value, six.string_types):
                try:
                    # Try to rip a date/datetime out of it.
                    value = make_aware(parse(value))
                except (ValueError, TypeError):
                    raise ApiFieldError(
                        "Datetime provided to '%s' field doesn't appear to be a valid datetime string: '%s'"
                        % (self.instance_name, value))

            else:
                raise ApiFieldError(
                    "Datetime provided to '%s' field must be a string: %s" %
                    (self.instance_name, value))

        return value
Ejemplo n.º 7
0
    def hydrate(self, bundle):
        value = super(DecimalField, self).hydrate(bundle)

        if value and not isinstance(value, Decimal):
            try:
                value = Decimal(value)
            except decimal.InvalidOperation:
                raise ApiFieldError(
                    "Invalid decimal string for '%s' field: '%s'" %
                    (self.instance_name, value))

        return value
Ejemplo n.º 8
0
    def convert(self, value):
        if value is None:
            return None

        if isinstance(value, six.string_types):
            try:
                year, month, day = value[:10].split('-')

                return datetime_safe.date(int(year), int(month), int(day))
            except ValueError:
                raise ApiFieldError(
                    "Date provided to '%s' field doesn't appear to be a valid date string: '%s'"
                    % (self.instance_name, value))

        return value
Ejemplo n.º 9
0
 def resource_from_uri(self,
                       fk_resource,
                       uri,
                       request=None,
                       related_obj=None,
                       related_name=None):
     try:
         obj = fk_resource.get_via_uri(uri, request=request)
         fk_resource = self.get_related_resource(obj)
         return super(GenericForeignKeyField,
                      self).resource_from_uri(fk_resource, uri, request,
                                              related_obj, related_name)
     except ObjectDoesNotExist:
         raise ApiFieldError(
             "Could not find the provided object via resource URI '%s'." %
             uri)
Ejemplo n.º 10
0
    def hydrate(self, bundle):
        """
        Takes data stored in the bundle for the field and returns it. Used for
        taking simple data and building a instance object.
        """
        if self.readonly:
            return None
        if self.instance_name not in bundle.data:
            if self.is_related and not self.is_m2m:
                # We've got an FK (or alike field) & a possible parent object.
                # Check for it.
                if bundle.related_obj and bundle.related_name in (
                        self.attribute, self.instance_name):
                    return bundle.related_obj
            if self.blank:
                return None
            if self.attribute:
                try:
                    val = getattr(bundle.obj, self.attribute, None)

                    if val is not None:
                        return val
                except ObjectDoesNotExist:
                    pass
            if self.instance_name:
                try:
                    if hasattr(bundle.obj, self.instance_name):
                        return getattr(bundle.obj, self.instance_name)
                except ObjectDoesNotExist:
                    pass
            if self.has_default():
                if callable(self._default):
                    return self._default()

                return self._default
            if self.null:
                return None

            raise ApiFieldError(
                "The '%s' field has no data and doesn't allow a default or null value."
                % self.instance_name)

        return bundle.data[self.instance_name]
Ejemplo n.º 11
0
    def build_related_resource(self,
                               value,
                               request=None,
                               related_obj=None,
                               related_name=None):
        """
        Returns a bundle of data built by the related resource, usually via
        ``hydrate`` with the data provided.

        Accepts either a URI, a data dictionary (or dictionary-like structure)
        or an object with a ``pk``.
        """
        fk_resource = self.to_class()
        kwargs = {
            'request': request,
            'related_obj': related_obj,
            'related_name': related_name,
        }

        if isinstance(value, Bundle):
            # Already hydrated, probably nested bundles. Just return.
            return value
        elif isinstance(value, six.string_types):
            # We got a URI. Load the object and assign it.
            return self.resource_from_uri(fk_resource, value, **kwargs)
        elif isinstance(value, dict):
            # We've got a data dictionary.
            # Since this leads to creation, this is the only one of these
            # methods that might care about "parent" data.
            return self.resource_from_data(fk_resource, value, **kwargs)
        elif hasattr(value, 'pk'):
            # We've got an object with a primary key.
            return self.resource_from_pk(fk_resource, value, **kwargs)
        else:
            raise ApiFieldError(
                "The '%s' field was given data that was not a URI, not a dictionary-alike and does not have a 'pk' attribute: %s."
                % (self.instance_name, value))
Ejemplo n.º 12
0
    def dehydrate(self, bundle, for_list=True):
        """
        Takes data from the provided object and prepares it for the
        resource.
        """
        if self.attribute is not None:
            current_object = bundle.obj

            for attr in self._attrs:
                previous_object = current_object
                current_object = getattr(current_object, attr, None)

                if current_object is None:
                    if self.null:
                        # Fall out of the loop, given any further attempts at
                        # accesses will fail miserably.
                        break
                    elif self.has_default():
                        current_object = self._default
                        # Fall out of the loop, given any further attempts at
                        # accesses will fail miserably.
                        break
                    else:
                        raise ApiFieldError(
                            "The object '%r' has an empty attribute '%s' and doesn't allow a default or null value."
                            % (previous_object, attr))

            if callable(current_object):
                current_object = current_object()

            return self.convert(current_object)

        if self.has_default():
            return self.convert(self.default)
        else:
            return None
Ejemplo n.º 13
0
    def resource_from_data(self,
                           fk_resource,
                           data,
                           request=None,
                           related_obj=None,
                           related_name=None):
        """
        Given a dictionary-like structure is provided, a fresh related
        resource is created using that data.
        """
        # Try to hydrate the data provided.
        data = dict_strip_unicode_keys(data)
        obj = None
        if getattr(fk_resource._meta, 'include_resource_uri',
                   True) and 'resource_uri' in data:
            uri = data['resource_uri']
            err_msg = "Could not find the provided %s object via resource URI '%s'." % (
                fk_resource._meta.resource_name,
                uri,
            )
            try:
                obj = fk_resource.get_via_uri(uri, request=request)
            except ObjectDoesNotExist:
                raise ApiFieldError(err_msg)

        fk_bundle = fk_resource.build_bundle(data=data,
                                             obj=obj,
                                             request=request)

        if related_obj:
            fk_bundle.related_obj = related_obj
            fk_bundle.related_name = related_name

        unique_keys = {
            k: v
            for k, v in data.items() if k == 'pk' or (
                hasattr(fk_resource, k) and getattr(fk_resource, k).unique)
        }

        # If we have no unique keys, we shouldn't go look for some resource that
        # happens to match other kwargs. In the case of a create, it might be the
        # completely wrong resource.
        # We also need to check to see if updates are allowed on the FK resource.
        if not obj and unique_keys:
            try:
                fk_resource.obj_get(fk_bundle, **data)
            except (ObjectDoesNotExist, NotFound, TypeError):
                try:
                    # Attempt lookup by primary key
                    fk_resource.obj_get(fk_bundle, **unique_keys)
                except (ObjectDoesNotExist, NotFound):
                    pass
            except MultipleObjectsReturned:
                pass

        # If we shouldn't update a resource, or we couldn't find a matching
        # resource we'll just return a populated bundle instead
        # of mistakenly updating something that should be read-only.
        fk_bundle = fk_resource.full_hydrate(fk_bundle)
        fk_resource.is_valid(fk_bundle)
        return fk_bundle