Esempio n. 1
0
def create_resource_from_dict(d, resource_name=None, full_clean=True):
    """
    Create a resource from a dict.
    """
    assert isinstance(d, dict)

    # Get the correct resource name
    document_resource_name = d.pop(RESOURCE_TYPE_FIELD, resource_name)
    if not (document_resource_name or resource_name):
        raise exceptions.ValidationError("Resource not defined.")

    resource_type = registration.get_resource(document_resource_name)
    if not resource_type:
        raise exceptions.ValidationError("Resource `%s` is not registered." % document_resource_name)

    # Check if we have an inherited type.
    if resource_name and not (resource_name == document_resource_name or
                              resource_name in resource_type._meta.parent_resource_names):
        raise exceptions.ValidationError(
            "Expected resource `%s` does not match resource defined in document `%s`." % (
                resource_name, document_resource_name))

    errors = {}
    attrs = {}
    for f in resource_type._meta.fields:
        value = d.pop(f.name, NOT_PROVIDED)
        try:
            attrs[f.attname] = f.clean(value)
        except exceptions.ValidationError as ve:
            errors[f.name] = ve.error_messages

    if errors:
        raise exceptions.ValidationError(errors)

    new_resource = resource_type(**attrs)
    if d:
        new_resource.extra_attrs(d)
    if full_clean:
        new_resource.full_clean()
    return new_resource
Esempio n. 2
0
def create_resource_from_dict(d,
                              resource=None,
                              full_clean=True,
                              copy_dict=True,
                              default_to_not_provided=False):
    # type: (Dict[str, Any], R, bool, bool, bool) -> Instance[R]
    """
    Create a resource from a dict.

    :param d: dictionary of data.
    :param resource: A resource type, resource name or list of resources and names to use as the base for creating a
        resource. If a list is supplied the first item will be used if a resource type is not supplied; this could also
        be a parent(s) of any resource defined by the dict.
    :param full_clean: Perform a full clean as part of the creation.
    :param copy_dict: Use a copy of the input dictionary rather than destructively processing the input dict.
    :param default_to_not_provided: If an value is not supplied keep the value as NOT_PROVIDED. This is used
        to support merging an updated value.

    """
    assert isinstance(d, dict)

    if copy_dict:
        d = d.copy()

    if resource:
        resource_type = None

        # Convert to single resource then resolve document type
        if isinstance(resource, (tuple, list)):
            resources = (resolve_resource_type(r) for r in resource)
        else:
            resources = [resolve_resource_type(resource)]

        for resource_name, type_field in resources:
            # See if the input includes a type field  and check it's registered
            document_resource_name = d.get(type_field, None)
            if document_resource_name:
                resource_type = registration.get_resource(
                    document_resource_name)
            else:
                resource_type = registration.get_resource(resource_name)

            if not resource_type:
                raise exceptions.ResourceException(
                    "Resource `%s` is not registered." %
                    document_resource_name)

            if document_resource_name:
                # Check resource types match or are inherited types
                if (resource_name == document_resource_name or resource_name
                        in getmeta(resource_type).parent_resource_names):
                    break  # We are done
            else:
                break

        if not resource_type:
            raise exceptions.ResourceException(
                "Incoming resource does not match [%s]" %
                ', '.join(r for r, t in resources))
    else:
        # No resource specified, relay on type field
        document_resource_name = d.pop(DEFAULT_TYPE_FIELD, None)
        if not document_resource_name:
            raise exceptions.ResourceException("Resource not defined.")

        # Get an instance of a resource type
        resource_type = registration.get_resource(document_resource_name)
        if not resource_type:
            raise exceptions.ResourceException(
                "Resource `%s` is not registered." % document_resource_name)

    attrs = []
    errors = {}
    meta = getmeta(resource_type)
    for f in meta.init_fields:
        value = d.pop(f.name, NotProvided)
        if value is NotProvided:
            if not default_to_not_provided:
                value = f.get_default(
                ) if f.use_default_if_not_provided else None
        else:
            try:
                value = f.to_python(value)
            except ValidationError as ve:
                errors[f.name] = ve.error_messages
        attrs.append(value)

    if errors:
        raise ValidationError(errors)

    new_resource = resource_type(*attrs)
    if d:
        new_resource.extra_attrs(d)
    if full_clean:
        new_resource.full_clean()
    return new_resource
Esempio n. 3
0
    def __new__(mcs, name, bases, attrs):
        super_new = super(ResourceType, mcs).__new__

        # attrs will never be empty for classes declared in the standard way
        # (ie. with the `class` keyword). This is quite robust.
        if name == 'NewBase' and attrs == {}:
            return super_new(mcs, name, bases, attrs)

        parents = [
            b for b in bases if isinstance(b, ResourceType)
            and not (b.__name__ == 'NewBase' and b.__mro__ == (b, object))
        ]
        if not parents:
            # If this isn't a subclass of Resource, don't do anything special.
            return super_new(mcs, name, bases, attrs)

        # Create the class.
        module = attrs.pop('__module__')
        new_class = super_new(mcs, name, bases, {'__module__': module})
        attr_meta = attrs.pop('Meta', None)
        abstract = getattr(attr_meta, 'abstract', False)
        if not attr_meta:
            meta = getattr(new_class, 'Meta', None)
        else:
            meta = attr_meta
        base_meta = getattr(new_class, '_meta', None)

        new_meta = mcs.meta_options(meta)
        new_class.add_to_class('_meta', new_meta)

        # Namespace is inherited
        if base_meta and new_meta.name_space is NotProvided:
            new_meta.name_space = base_meta.name_space

        # Generate a namespace if one is not provided
        if new_meta.name_space is NotProvided:
            new_meta.name_space = module

        # Key field is inherited
        if base_meta and new_meta.key_field_names is None:
            new_meta.key_field_names = base_meta.key_field_names

        # Field sorting is inherited
        if new_meta.field_sorting is NotProvided:
            new_meta.field_sorting = base_meta.field_sorting if base_meta else False

        # Bail out early if we have already created this class.
        r = registration.get_resource(new_meta.resource_name)
        if r is not None:
            return r

        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)

        # Sort the fields
        if not new_meta.field_sorting:
            new_meta.fields = sorted(new_meta.fields, key=hash)

        # All the fields of any type declared on this model
        local_field_attnames = set([f.attname for f in new_meta.fields])
        field_attnames = set(local_field_attnames)

        for base in parents:
            try:
                base_meta = getattr(base, '_meta')
            except AttributeError:
                # Things without _meta aren't functional models, so they're
                # uninteresting parents.
                continue

            # Check for clashes between locally declared fields and those
            # on the base classes (we cannot handle shadowed fields at the
            # moment).
            for field in base_meta.all_fields:
                if field.attname in local_field_attnames:
                    raise Exception(
                        'Local field %r in class %r clashes with field of similar name from '
                        'base class %r' % (field.attname, name, base.__name__))
            for field in base_meta.fields:
                if field.attname not in field_attnames:
                    field_attnames.add(field.attname)
                    new_class.add_to_class(field.attname, copy.deepcopy(field))
            for field in base_meta.virtual_fields:
                new_class.add_to_class(field.attname, copy.deepcopy(field))

            new_meta.parents += base_meta.parents
            new_meta.parents.append(base)

        # Sort the fields
        if new_meta.field_sorting:
            if callable(new_meta.field_sorting):
                new_meta.fields = new_meta.field_sorting(new_meta.fields)
            else:
                new_meta.fields = sorted(new_meta.fields, key=hash)

        # If a key_field is defined ensure it exists
        if new_meta.key_field_names:
            for field_name in new_meta.key_field_names:
                if field_name not in new_meta.field_map:
                    raise AttributeError(
                        'Key field `{0}` is not exist on this resource.'.
                        format(field_name))

        # Give fields an opportunity to do additional operations after the
        # resource is full populated and ready.
        for field in new_meta.all_fields:
            if hasattr(field, 'on_resource_ready'):
                field.on_resource_ready()

        if abstract:
            return new_class

        # Register resource
        registration.register_resources(new_class)

        # Because of the way imports happen (recursively), we may or may not be
        # the first time this model tries to register with the framework. There
        # should only be one class for each model, so we always return the
        # registered version.
        return registration.get_resource(new_meta.resource_name)
Esempio n. 4
0
def create_resource_from_dict(d, resource=None, full_clean=True, copy_dict=True, default_to_not_provided=False):
    """
    Create a resource from a dict.

    :param d: dictionary of data.
    :param resource: A resource type, resource name or list of resources and names to use as the base for creating a
        resource. If a list is supplied the first item will be used if a resource type is not supplied; this could also
        be a parent(s) of any resource defined by the dict.
    :param full_clean: Do a full clean as part of the creation.
    :param copy_dict: Use a copy of the input dictionary rather than destructively processing the input dict.
    :param default_to_not_supplied: If an value is not supplied keep the value as NOT_PROVIDED. This is used
        to support merging an updated value.

    """
    assert isinstance(d, dict)

    if copy_dict:
        d = d.copy()

    if resource:
        resource_type = None

        # Convert to single resource then resolve document type
        if isinstance(resource, (tuple, list)):
            resources = (resolve_resource_type(r) for r in resource)
        else:
            resources = [resolve_resource_type(resource)]

        for resource_name, type_field in resources:
            # See if the input includes a type field  and check it's registered
            document_resource_name = d.get(type_field, None)
            if document_resource_name:
                resource_type = registration.get_resource(document_resource_name)
            else:
                resource_type = registration.get_resource(resource_name)

            if not resource_type:
                raise exceptions.ResourceException("Resource `%s` is not registered." % document_resource_name)

            if document_resource_name:
                # Check resource types match or are inherited types
                if (resource_name == document_resource_name or
                        resource_name in resource_type._meta.parent_resource_names):
                    break  # We are done
            else:
                break

        if not resource_type:
            raise exceptions.ResourceException(
                "Incoming resource does not match [%s]" % ', '.join(r for r, t in resources))
    else:
        # No resource specified, relay on type field
        document_resource_name = d.pop(DEFAULT_TYPE_FIELD, None)
        if not document_resource_name:
            raise exceptions.ResourceException("Resource not defined.")

        # Get an instance of a resource type
        resource_type = registration.get_resource(document_resource_name)
        if not resource_type:
            raise exceptions.ResourceException("Resource `%s` is not registered." % document_resource_name)

    attrs = []
    errors = {}
    for f in resource_type._meta.fields:
        value = d.pop(f.name, NOT_PROVIDED)
        if value is NOT_PROVIDED:
            if not default_to_not_provided:
                value = f.get_default() if f.use_default_if_not_provided else None
        else:
            try:
                value = f.to_python(value)
            except ValidationError as ve:
                errors[f.name] = ve.error_messages
        attrs.append(value)

    if errors:
        raise ValidationError(errors)

    new_resource = resource_type(*attrs)
    if d:
        new_resource.extra_attrs(d)
    if full_clean:
        new_resource.full_clean()
    return new_resource
Esempio n. 5
0
    def __new__(cls, name, bases, attrs):
        super_new = super(ResourceBase, cls).__new__

        # attrs will never be empty for classes declared in the standard way
        # (ie. with the `class` keyword). This is quite robust.
        if name == 'NewBase' and attrs == {}:
            return super_new(cls, name, bases, attrs)

        parents = [
            b for b in bases if
            isinstance(b, ResourceBase) and not (b.__name__ == 'NewBase' and b.__mro__ == (b, object))
        ]
        if not parents:
            # If this isn't a subclass of Resource, don't do anything special.
            return super_new(cls, name, bases, attrs)

        # Create the class.
        module = attrs.pop('__module__')
        new_class = super_new(cls, name, bases, {'__module__': module})
        attr_meta = attrs.pop('Meta', None)
        abstract = getattr(attr_meta, 'abstract', False)
        if not attr_meta:
            meta = getattr(new_class, 'Meta', None)
        else:
            meta = attr_meta
        base_meta = getattr(new_class, '_meta', None)

        new_class.add_to_class('_meta', ResourceOptions(meta))

        # Generate a namespace if one is not provided
        if new_class._meta.name_space is NOT_PROVIDED and base_meta:
            # Namespace is inherited
            if (not new_class._meta.name_space) or (new_class._meta.name_space is NOT_PROVIDED):
                new_class._meta.name_space = base_meta.name_space

        if new_class._meta.name_space is NOT_PROVIDED:
            new_class._meta.name_space = module

        # Key field is inherited
        if base_meta and (not new_class._meta.key_field_name) or (new_class._meta.key_field_name is NOT_PROVIDED):
            new_class._meta.key_field_name = base_meta.key_field_name

        # Bail out early if we have already created this class.
        r = registration.get_resource(new_class._meta.resource_name)
        if r is not None:
            return r

        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)

        # Sort the fields
        new_class._meta.fields = sorted(new_class._meta.fields, key=hash)

        # All the fields of any type declared on this model
        local_field_attnames = set([f.attname for f in new_class._meta.fields])
        field_attnames = set(local_field_attnames)

        for base in parents:
            if not hasattr(base, '_meta'):
                # Things without _meta aren't functional models, so they're
                # uninteresting parents.
                continue

            # Check for clashes between locally declared fields and those
            # on the base classes (we cannot handle shadowed fields at the
            # moment).
            for field in base._meta.all_fields:
                if field.attname in local_field_attnames:
                    raise Exception('Local field %r in class %r clashes with field of similar name from '
                                    'base class %r' % (field.attname, name, base.__name__))
            for field in base._meta.fields:
                if field.attname not in field_attnames:
                    field_attnames.add(field.attname)
                    new_class.add_to_class(field.attname, copy.deepcopy(field))
            for field in base._meta.virtual_fields:
                new_class.add_to_class(field.attname, copy.deepcopy(field))

            new_class._meta.parents += base._meta.parents
            new_class._meta.parents.append(base)

        # If a key_field is defined ensure it exists
        if new_class._meta.key_field_name is not None and new_class._meta.key_field is None:
                raise AttributeError('Key field `{}` is not exist on this resource.'.format(
                    new_class._meta.key_field_name)
                )

        if abstract:
            return new_class

        # Register resource
        registration.register_resources(new_class)

        # Because of the way imports happen (recursively), we may or may not be
        # the first time this model tries to register with the framework. There
        # should only be one class for each model, so we always return the
        # registered version.
        return registration.get_resource(new_class._meta.resource_name)
Esempio n. 6
0
    def __new__(mcs, name, bases, attrs):
        super_new = super(ResourceProxyType, mcs).__new__

        # attrs will never be empty for classes declared in the standard way
        # (ie. with the `class` keyword). This is quite robust.
        if name == 'NewBase' and attrs == {}:
            return super_new(mcs, name, bases, attrs)

        parents = [
            b for b in bases if
            isinstance(b, ResourceProxyType) and not (b.__name__ == 'NewBase' and b.__mro__ == (b, object))
        ]
        if not parents:
            # If this isn't a subclass of Resource, don't do anything special.
            return super_new(mcs, name, bases, attrs)

        # Create the class.
        module = attrs.pop('__module__')
        new_class = super_new(mcs, name, bases, {'__module__': module})
        attr_meta = attrs.pop('Meta', None)
        if not attr_meta:
            meta = getattr(new_class, 'Meta', None)
        else:
            meta = attr_meta

        new_meta = mcs.meta_options(meta)
        new_class.add_to_class('_meta', new_meta)

        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)

        # Determine which fields will be shadowed.
        field_names, readonly = filter_fields(
            new_meta.shadow.field_map,
            new_meta.include,
            new_meta.exclude,
            new_meta.readonly
        )

        # Map field names
        new_meta.fields = [f for f in new_meta.shadow.fields if f.attname in field_names]

        # Sort the fields
        if new_meta.field_sorting:
            if callable(new_meta.field_sorting):
                new_meta.fields = new_meta.field_sorting(new_meta.fields)
            else:
                new_meta.fields = sorted(new_meta.fields, key=hash)

        # Generate field descriptors
        for field in new_meta.fields:
            new_class.add_to_class(field.attname, FieldProxyDescriptor(field in new_meta.readonly_fields))

        # If a key_field is defined ensure it exists
        if new_meta.key_field_names:
            for field_name in new_meta.key_field_names:
                if field_name not in new_meta.field_map:
                    raise AttributeError('Key field `{0}` is not exist on this resource.'.format(field_name))

        # Register resource
        registration.register_resources(new_class)

        # Because of the way imports happen (recursively), we may or may not be
        # the first time this model tries to register with the framework. There
        # should only be one class for each model, so we always return the
        # registered version.
        return registration.get_resource(new_meta.resource_name)
Esempio n. 7
0
    def __new__(mcs, name, bases, attrs):
        super_new = super(ResourceProxyType, mcs).__new__

        # attrs will never be empty for classes declared in the standard way
        # (ie. with the `class` keyword). This is quite robust.
        if name == 'NewBase' and attrs == {}:
            return super_new(mcs, name, bases, attrs)

        parents = [
            b for b in bases if isinstance(b, ResourceProxyType)
            and not (b.__name__ == 'NewBase' and b.__mro__ == (b, object))
        ]
        if not parents:
            # If this isn't a subclass of Resource, don't do anything special.
            return super_new(mcs, name, bases, attrs)

        # Create the class.
        module = attrs.pop('__module__')
        new_class = super_new(mcs, name, bases, {'__module__': module})
        attr_meta = attrs.pop('Meta', None)
        if not attr_meta:
            meta = getattr(new_class, 'Meta', None)
        else:
            meta = attr_meta

        new_meta = mcs.meta_options(meta)
        new_class.add_to_class('_meta', new_meta)

        # Add all attributes to the class.
        for obj_name, obj in attrs.items():
            new_class.add_to_class(obj_name, obj)

        # Determine which fields will be shadowed.
        field_names, readonly = filter_fields(new_meta.shadow.field_map,
                                              new_meta.include,
                                              new_meta.exclude,
                                              new_meta.readonly)

        # Map field names
        new_meta.fields = [
            f for f in new_meta.shadow.fields if f.attname in field_names
        ]

        # Sort the fields
        if new_meta.field_sorting:
            if callable(new_meta.field_sorting):
                new_meta.fields = new_meta.field_sorting(new_meta.fields)
            else:
                new_meta.fields = sorted(new_meta.fields, key=hash)

        # Generate field descriptors
        for field in new_meta.fields:
            new_class.add_to_class(
                field.attname,
                FieldProxyDescriptor(field in new_meta.readonly_fields))

        # If a key_field is defined ensure it exists
        if new_meta.key_field_names:
            for field_name in new_meta.key_field_names:
                if field_name not in new_meta.field_map:
                    raise AttributeError(
                        'Key field `{0}` is not exist on this resource.'.
                        format(field_name))

        # Register resource
        registration.register_resources(new_class)

        # Because of the way imports happen (recursively), we may or may not be
        # the first time this model tries to register with the framework. There
        # should only be one class for each model, so we always return the
        # registered version.
        return registration.get_resource(new_meta.resource_name)