Exemplo n.º 1
0
def _generate_auto_mapping(name, from_fields, to_fields):
    """
    Generate the auto mapping between two fields.
    """
    from_field = from_fields[name]
    to_field = to_fields[name]
    name = force_tuple(name)

    # Handle ListOf fields
    if isinstance(from_field, ListOf) and isinstance(to_field, ListOf):
        try:
            mapping = registration.get_mapping(from_field.of, to_field.of)
            return define(name, MapListOf(mapping), name, to_list=False, bind=True)
        except KeyError:
            # If both items are from and to fields refer to the same object automatically use a mapper that just
            # produces a clone.
            if from_field.of is to_field.of:
                return define(name, MapListOf(NoOpMapper), name, to_list=False, bind=True)

    # Handle DictAs fields
    elif isinstance(from_field, DictAs) and isinstance(to_field, DictAs):
        try:
            mapping = registration.get_mapping(from_field.of, to_field.of)
            return define(name, MapDictAs(mapping), name, bind=True)
        except KeyError:
            # If both items are from and to fields refer to the same object automatically use a mapper that just
            # produces a clone.
            if from_field.of is to_field.of:
                return define(name, MapDictAs(NoOpMapper), name, bind=True)

    return define(name, None, name)
Exemplo n.º 2
0
    def __init__(self, *args, **kwargs):
        super(ModelResourceApi, self).__init__(*args, **kwargs)

        assert self.model, "A model has not been provided."

        # Attempt to resolve mappings
        if self.to_model_mapping is None:
            self.to_model_mapping = registration.get_mapping(self.resource, self.model)
        if self.to_resource_mapping is None:
            self.to_resource_mapping = registration.get_mapping(self.model, self.resource)
Exemplo n.º 3
0
    def from_model(cls, model, context=None, **field_values):
        """
        Convert this resource into a specified to resource.

        A mapping must be defined for conversion between this resource and to_resource or an exception will be raised.
        """
        mapping = registration.get_mapping(cls.model, cls)
        return mapping(model, context).convert(**field_values)
Exemplo n.º 4
0
    def from_model(cls, model, context=None, **field_values):
        """
        Convert this resource into a specified to resource.

        A mapping must be defined for conversion between this resource and to_resource or an exception will be raised.
        """
        mapping = registration.get_mapping(cls.model, cls)
        return mapping(model, context).convert(**field_values)
Exemplo n.º 5
0
 def generate_dict_to_dict_mapping(mcs, name, from_field, to_field):
     try:
         mapping = registration.get_mapping(from_field.of, to_field.of)
         return define(name, MapDictAs(mapping), name, bind=True)
     except KeyError:
         # If both items are from and to fields refer to the same object automatically use a mapper that just
         # produces a clone.
         if from_field.of is to_field.of:
             return define(name, MapDictAs(NoOpMapper), name, bind=True)
Exemplo n.º 6
0
    def convert_to(self, to_resource, **field_values):
        """
        Convert this resource into a specified to resource.

        A mapping must be defined for conversion between this resource and to_resource or an exception will be raised.
        """
        self.full_clean()
        mapping = registration.get_mapping(self.__class__, to_resource)
        return mapping(self).convert(**field_values)
Exemplo n.º 7
0
 def generate_dict_to_dict_mapping(mcs, name, from_field, to_field):
     try:
         mapping = registration.get_mapping(from_field.of, to_field.of)
         return define(name, MapDictAs(mapping), name, bind=True)
     except KeyError:
         # If both items are from and to fields refer to the same object automatically use a mapper that just
         # produces a clone.
         if from_field.of is to_field.of:
             return define(name, MapDictAs(NoOpMapper), name, bind=True)
Exemplo n.º 8
0
    def update_existing(self, dest_obj, context=None, ignore_fields=None):
        """
        Update the fields on an existing destination object.

        A mapping must be defined for conversion between this resource and ``dest_obj`` type or an exception will be
        raised.

        """
        self.full_clean(ignore_fields)
        mapping = registration.get_mapping(self.__class__, dest_obj.__class__)
        return mapping(self, context).update(dest_obj, ignore_fields)
Exemplo n.º 9
0
    def update_existing(self, dest_obj, context=None, ignore_fields=None):
        """
        Update the fields on an existing destination object.

        A mapping must be defined for conversion between this resource and ``dest_obj`` type or an exception will be
        raised.

        """
        self.full_clean(ignore_fields)
        mapping = registration.get_mapping(self.__class__, dest_obj.__class__)
        return mapping(self, context).update(dest_obj, ignore_fields)
Exemplo n.º 10
0
    def convert_to(self, to_resource, context=None, ignore_fields=None, **field_values):
        """
        Convert this resource into a specified resource.

        A mapping must be defined for conversion between this resource and to_resource or an exception will be raised.

        """
        mapping = registration.get_mapping(self.__class__, to_resource)
        ignore_fields = ignore_fields or []
        ignore_fields.extend(mapping.exclude_fields)
        self.full_clean(ignore_fields)
        return mapping(self, context).convert(**field_values)
Exemplo n.º 11
0
 def generate_list_to_list_mapping(mcs, name, from_field, to_field):
     """
     Generate a mapping of list to list objects.
     """
     try:
         mapping = registration.get_mapping(from_field.of, to_field.of)
         return FieldMapping(name, MapListOf(mapping), name, to_list=False, bind=True, skip_if_none=False)
     except KeyError:
         # If both items are from and to fields refer to the same object automatically use a mapper that just
         # produces a clone.
         if from_field.of is to_field.of:
             return FieldMapping(name, MapListOf(NoOpMapper), name, to_list=False, bind=True, skip_if_none=False)
Exemplo n.º 12
0
def _generate_auto_mapping(name, from_fields, to_fields):
    """
    Generate the auto mapping between two fields.
    """
    from_field = from_fields[name]
    to_field = to_fields[name]
    name = force_tuple(name)

    # Handle ListOf fields
    if isinstance(from_field, ListOf) and isinstance(to_field, ListOf):
        try:
            mapping = registration.get_mapping(from_field.of, to_field.of)
            return define(name,
                          MapListOf(mapping),
                          name,
                          to_list=False,
                          bind=True)
        except KeyError:
            # If both items are from and to fields refer to the same object automatically use a mapper that just
            # produces a clone.
            if from_field.of is to_field.of:
                return define(name,
                              MapListOf(NoOpMapper),
                              name,
                              to_list=False,
                              bind=True)

    # Handle DictAs fields
    elif isinstance(from_field, DictAs) and isinstance(to_field, DictAs):
        try:
            mapping = registration.get_mapping(from_field.of, to_field.of)
            return define(name, MapDictAs(mapping), name, bind=True)
        except KeyError:
            # If both items are from and to fields refer to the same object automatically use a mapper that just
            # produces a clone.
            if from_field.of is to_field.of:
                return define(name, MapDictAs(NoOpMapper), name, bind=True)

    return define(name, None, name)
Exemplo n.º 13
0
    def convert_to(self,
                   to_resource,
                   context=None,
                   ignore_fields=None,
                   **field_values):
        """
        Convert this resource into a specified resource.

        A mapping must be defined for conversion between this resource and to_resource or an exception will be raised.

        """
        mapping = registration.get_mapping(self.__class__, to_resource)
        ignore_fields = ignore_fields or []
        ignore_fields.extend(mapping.exclude_fields)
        self.full_clean(ignore_fields)
        return mapping(self, context).convert(**field_values)
Exemplo n.º 14
0
    def update(self, instance, context=None, commit=True, lazy=True):
        """
        Update an existing model from this model.

        :param instance: Model instance to be updated.
        :param context: Context dict passed to each mapping function.
        :param commit: If True, then changes to the updated model instance will be saved to the database.
        :param lazy: If True, then instance and source resource are compared, if no changes are found no database,
            operation is performed.
        :return: Updated model instance.

        """
        assert isinstance(instance, self.model)

        mapping = registration.get_mapping(self.__class__, self.model)
        mapper = mapping(self, context)

        if not lazy or len(mapper.diff(instance)) > 0:
            mapper.update(instance)
            if commit:
                instance.save()
        return instance
Exemplo n.º 15
0
 def generate_list_to_list_mapping(mcs, name, from_field, to_field):
     """
     Generate a mapping of list to list objects.
     """
     try:
         mapping = registration.get_mapping(from_field.of, to_field.of)
         return FieldMapping(name,
                             MapListOf(mapping),
                             name,
                             to_list=False,
                             bind=True,
                             skip_if_none=False)
     except KeyError:
         # If both items are from and to fields refer to the same object automatically use a mapper that just
         # produces a clone.
         if from_field.of is to_field.of:
             return FieldMapping(name,
                                 MapListOf(NoOpMapper),
                                 name,
                                 to_list=False,
                                 bind=True,
                                 skip_if_none=False)
Exemplo n.º 16
0
    def update(self, instance, context=None, commit=True, lazy=True):
        """
        Update an existing model from this model.

        :param instance: Model instance to be updated.
        :param context: Context dict passed to each mapping function.
        :param commit: If True, then changes to the updated model instance will be saved to the database.
        :param lazy: If True, then instance and source resource are compared, if no changes are found no database,
            operation is performed.
        :return: Updated model instance.

        """
        assert isinstance(instance, self.model)

        mapping = registration.get_mapping(self.__class__, self.model)
        mapper = mapping(self, context)

        if not lazy or len(mapper.diff(instance)) > 0:
            mapper.update(instance)
            if commit:
                instance.save()
        return instance
Exemplo n.º 17
0
    def __new__(cls, name, bases, attrs):
        super_new = super(MappingBase, 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, MappingBase) and not (b.__name__ == 'NewBase'
                                                                           and b.__mro__ == (b, object))]
        if not parents:
            # If this isn't a subclass of Mapping, don't do anything special.
            return super_new(cls, name, bases, attrs)

        from_resource = attrs.get('from_resource')
        if not (isinstance(from_resource, type) and issubclass(from_resource, Resource)):
            raise MappingSetupError('`from_resource` is not a Resource type')

        to_resource = attrs.get('to_resource')
        if not (isinstance(to_resource, type) and issubclass(to_resource, Resource)):
            raise MappingSetupError('`to_resource` is not a Resource type')

        # Check if we have already created this mapping
        try:
            return registration.get_mapping(from_resource, to_resource)
        except KeyError:
            pass  # Not registered

        # Generate mapping rules.
        from_fields = from_resource._meta.field_map
        to_fields = to_resource._meta.field_map

        def attr_mapping_to_mapping_rule(m, def_type, ref):
            # Parse, validate and normalise defined mapping rules so rules can be executed without having to
            # perform checks during a mapping operation.
            to_list = False
            try:
                map_from, action, map_to, to_list = m
            except ValueError:
                try:
                    map_from, action, map_to = m
                except ValueError:
                    raise MappingSetupError('Bad mapping definition `%s` in %s `%s`.' % (m, def_type, ref))

            map_from = force_tuple(map_from)
            for f in map_from:
                if not f in from_fields:
                    raise MappingSetupError('Field `%s` of %s `%s` not found on from resource. ' % (f, def_type, ref))

            if isinstance(action, six.string_types):
                if action not in attrs:
                    raise MappingSetupError('Action named %s defined in %s `%s` was not defined on mapping object.' % (
                        action, def_type, ref))
                if not callable(attrs[action]):
                    raise MappingSetupError('Action named %s defined in %s `%s` is not callable.' % (
                        action, def_type, ref))
            elif action is not None and not callable(action):
                raise MappingSetupError('Action on %s `%s` is not callable.' % (def_type, ref))

            map_to = force_tuple(map_to)
            if to_list and len(map_to) != 1:
                raise MappingSetupError('The %s `%s` specifies a to_list mapping, these can only be applied to a '
                                        'single target field.' % (def_type, m))
            for f in map_to:
                if not f in to_fields:
                    raise MappingSetupError('Field `%s` of %s `%s` not found on to resource. ' % (f, def_type, ref))

            return map_from, action, map_to, to_list

        def remove_from_unmapped_fields(rule):
            # Remove any fields that are handled by a mapping rule from unmapped_fields list.
            map_from, _, map_to, _ = rule
            if len(map_from) == 1 and map_from[0] in unmapped_fields:
                unmapped_fields.remove(map_from[0])
            if len(map_to) == 1 and map_to[0] in unmapped_fields:
                unmapped_fields.remove(map_to[0])

        exclude_fields = attrs.get('exclude_fields') or tuple()
        unmapped_fields = [attname for attname in from_fields if attname not in exclude_fields]
        mapping_rules = []

        # Add basic mappings
        for idx, mapping in enumerate(attrs.pop('mappings', [])):
            mapping_rule = attr_mapping_to_mapping_rule(mapping, 'basic mapping', idx)
            mapping_rules.append(mapping_rule)
            remove_from_unmapped_fields(mapping_rule)

        # Add custom mappings
        for attr in attrs.values():
            # Methods with a _mapping attribute have been decorated by either `map_field` or `map_list_field`
            # decorators.
            if hasattr(attr, '_mapping'):
                mapping_rule = attr_mapping_to_mapping_rule(getattr(attr, '_mapping'), 'custom mapping', attr)
                mapping_rules.append(mapping_rule)
                remove_from_unmapped_fields(mapping_rule)
                # Remove mapping
                delattr(attr, '_mapping')

        # Add auto mapped fields that are yet to be mapped.
        for field in unmapped_fields:
            if field in to_fields:
                mapping_rules.append(((field,), None, (field,), False))

        # Update mappings
        attrs['_mapping_rules'] = mapping_rules

        registration.register_mappings(super_new(cls, name, bases, attrs))
        return registration.get_mapping(from_resource, to_resource)
Exemplo n.º 18
0
    def __new__(mcs, name, bases, attrs):
        super_new = super(MappingMeta, 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, MappingMeta)
            and not (b.__name__ == 'NewBase' and b.__mro__ ==
                     (b, MappingBase, object))
        ]
        if not parents:
            # If this isn't a subclass of Mapping, don't do anything special.
            return super_new(mcs, name, bases, attrs)

        # Backward compatibility from_resource -> from_obj
        from_obj = attrs.setdefault('from_obj', attrs.get('from_resource'))
        if from_obj is None:
            raise MappingSetupError('`from_obj` is not defined.')
        to_obj = attrs.setdefault('to_obj', attrs.get('to_resource'))
        if to_obj is None:
            raise MappingSetupError('`to_obj` is not defined.')
        register_mapping = attrs.pop('register_mapping', True)

        # Check if we have already created this mapping
        if register_mapping:
            try:
                return registration.get_mapping(from_obj, to_obj)
            except KeyError:
                pass  # Not registered

        # Get field resolver objects
        try:
            from_fields = registration.get_field_resolver(
                from_obj).from_field_dict
        except KeyError:
            raise MappingSetupError(
                '`from_obj` %r does not have an attribute resolver defined.' %
                from_obj)
        try:
            to_fields = registration.get_field_resolver(to_obj).to_field_dict
        except KeyError:
            raise MappingSetupError(
                '`to_obj` %r does not have an attribute resolver defined.' %
                to_obj)

        def attr_mapping_to_mapping_rule(m, def_type, ref):
            """ Parse, validate and normalise defined mapping rules so rules can be executed without having to perform
            checks during a mapping operation."""
            to_list = False
            bind = False
            skip_if_none = False
            is_assignment = False
            try:
                map_from, action, map_to, to_list, bind, skip_if_none = m
            except ValueError:
                try:
                    map_from, action, map_to = m
                except ValueError:
                    raise MappingSetupError(
                        'Bad mapping definition `%s` in %s `%s`.' %
                        (m, def_type, ref))

            if map_from is None:
                is_assignment = True

            if not is_assignment:
                map_from = force_tuple(map_from)
                for f in map_from:
                    if f not in from_fields:
                        raise MappingSetupError(
                            'Field `%s` of %s `%s` not found on from object. '
                            % (f, def_type, ref))

            if isinstance(action, six.string_types):
                if action not in attrs:
                    raise MappingSetupError(
                        'Action named %s defined in %s `%s` was not defined on mapping object.'
                        % (action, def_type, ref))
                if not callable(attrs[action]):
                    raise MappingSetupError(
                        'Action named %s defined in %s `%s` is not callable.' %
                        (action, def_type, ref))
            elif action is not None and not callable(action):
                raise MappingSetupError('Action on %s `%s` is not callable.' %
                                        (def_type, ref))
            elif action is None and is_assignment:
                raise MappingSetupError(
                    'No action supplied for `%s` in `%s`.' % (def_type, ref))

            map_to = force_tuple(map_to)
            if to_list and len(map_to) != 1:
                raise MappingSetupError(
                    'The %s `%s` specifies a to_list mapping, these can only be applied to a '
                    'single target field.' % (def_type, m))
            for f in map_to:
                if f not in to_fields:
                    raise MappingSetupError(
                        'Field `%s` of %s `%s` not found on to object. ' %
                        (f, def_type, ref))

            return FieldMapping(map_from, action, map_to, to_list, bind,
                                skip_if_none)

        # Determine what fields need to have mappings generated
        exclude_fields = attrs.get('exclude_fields') or tuple()
        unmapped_fields = [
            attname for attname in from_fields if attname not in exclude_fields
        ]

        def remove_from_unmapped_fields(rule):
            # Remove any fields that are handled by a mapping rule from unmapped_fields list.
            map_to = rule[2]
            if len(map_to) == 1 and map_to[0] in unmapped_fields:
                unmapped_fields.remove(map_to[0])

        # Generate mapping rules.
        mapping_rules = []

        # Check that from_obj is a sub_class (or same class) as any `parent.from_obj`. This is important for mapping
        # sub class lists and resolving mappings.
        base_parents = [p for p in parents if hasattr(p, '_subs')]
        for p in base_parents:
            if not issubclass(from_obj, p.from_obj):
                raise MappingSetupError(
                    '`from_obj` must be a subclass of `parent.from_obj`')
            if not issubclass(to_obj, p.to_obj):
                raise MappingSetupError(
                    '`to_obj` must be a subclass of `parent.to_obj`')

            # Copy mapping rules
            for mapping_rule in p._mapping_rules:
                mapping_rules.append(mapping_rule)
                remove_from_unmapped_fields(mapping_rule)

        # Add basic mappings
        for idx, mapping in enumerate(attrs.pop('mappings', [])):
            mapping_rule = attr_mapping_to_mapping_rule(
                mapping, 'basic mapping', idx)
            mapping_rules.append(mapping_rule)
            remove_from_unmapped_fields(mapping_rule)

        # Add custom mappings
        for attr in attrs.values():
            # Methods with a _mapping attribute have been decorated by either `map_field` or `map_list_field`
            # decorators.
            if hasattr(attr, '_mapping'):
                mapping_rule = attr_mapping_to_mapping_rule(
                    getattr(attr, '_mapping'), 'custom mapping', attr)
                mapping_rules.append(mapping_rule)
                remove_from_unmapped_fields(mapping_rule)
                # Remove mapping
                delattr(attr, '_mapping')

        # Add auto mapped fields that are yet to be mapped.
        for field in unmapped_fields:
            if field in to_fields:
                mapping_rules.append(
                    mcs.generate_auto_mapping(field, from_fields, to_fields))

        # Update attributes
        attrs['_mapping_rules'] = mapping_rules
        attrs['_subs'] = {}

        # Create mapper instance
        mapper = super_new(mcs, name, bases, attrs)
        if register_mapping:
            registration.register_mapping(mapper)
            mapper = registration.get_mapping(from_obj, to_obj)

            # Register mapping with parents mapping objects as a sub class.
            for parent in base_parents:
                parent._subs[from_obj] = mapper

        return mapper
Exemplo n.º 19
0
    def __new__(mcs, name, bases, attrs):
        super_new = super(MappingMeta, 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, MappingMeta) and not (b.__name__ == 'NewBase' and b.__mro__ == (b, MappingBase, object))
        ]
        if not parents:
            # If this isn't a subclass of Mapping, don't do anything special.
            return super_new(mcs, name, bases, attrs)

        # Backward compatibility from_resource -> from_obj
        from_obj = attrs.setdefault('from_obj', attrs.get('from_resource'))
        if from_obj is None:
            raise MappingSetupError('`from_obj` is not defined.')
        to_obj = attrs.setdefault('to_obj', attrs.get('to_resource'))
        if to_obj is None:
            raise MappingSetupError('`to_obj` is not defined.')
        register_mapping = attrs.pop('register_mapping', True)

        # Check if we have already created this mapping
        if register_mapping:
            try:
                return registration.get_mapping(from_obj, to_obj)
            except KeyError:
                pass  # Not registered

        # Get field resolver objects
        try:
            from_fields = registration.get_field_resolver(from_obj).from_field_dict
        except KeyError:
            raise MappingSetupError('`from_obj` %r does not have an attribute resolver defined.' % from_obj)
        try:
            to_fields = registration.get_field_resolver(to_obj).to_field_dict
        except KeyError:
            raise MappingSetupError('`to_obj` %r does not have an attribute resolver defined.' % to_obj)

        def attr_mapping_to_mapping_rule(m, def_type, ref):
            """ Parse, validate and normalise defined mapping rules so rules can be executed without having to perform
            checks during a mapping operation."""
            to_list = False
            bind = False
            skip_if_none = False
            is_assignment = False
            try:
                map_from, action, map_to, to_list, bind, skip_if_none = m
            except ValueError:
                try:
                    map_from, action, map_to = m
                except ValueError:
                    raise MappingSetupError('Bad mapping definition `%s` in %s `%s`.' % (m, def_type, ref))

            if map_from is None:
                is_assignment = True

            if not is_assignment:
                map_from = force_tuple(map_from)
                for f in map_from:
                    if f not in from_fields:
                        raise MappingSetupError('Field `%s` of %s `%s` not found on from object. ' % (f, def_type, ref))

            if isinstance(action, six.string_types):
                if action not in attrs:
                    raise MappingSetupError('Action named %s defined in %s `%s` was not defined on mapping object.' % (
                        action, def_type, ref))
                if not callable(attrs[action]):
                    raise MappingSetupError('Action named %s defined in %s `%s` is not callable.' % (
                        action, def_type, ref))
            elif action is not None and not callable(action):
                raise MappingSetupError('Action on %s `%s` is not callable.' % (def_type, ref))
            elif action is None and is_assignment:
                raise MappingSetupError('No action supplied for `%s` in `%s`.' % (def_type, ref))

            map_to = force_tuple(map_to)
            if to_list and len(map_to) != 1:
                raise MappingSetupError('The %s `%s` specifies a to_list mapping, these can only be applied to a '
                                        'single target field.' % (def_type, m))
            for f in map_to:
                if f not in to_fields:
                    raise MappingSetupError('Field `%s` of %s `%s` not found on to object. ' % (f, def_type, ref))

            return FieldMapping(map_from, action, map_to, to_list, bind, skip_if_none)

        # Determine what fields need to have mappings generated
        exclude_fields = attrs.get('exclude_fields') or ()
        unmapped_fields = [attname for attname in from_fields if attname not in exclude_fields]

        def remove_from_unmapped_fields(rule):
            # Remove any fields that are handled by a mapping rule from unmapped_fields list.
            map_to = rule[2]
            if len(map_to) == 1 and map_to[0] in unmapped_fields:
                unmapped_fields.remove(map_to[0])

        # Generate mapping rules.
        mapping_rules = []

        # Check that from_obj is a sub_class (or same class) as any `parent.from_obj`. This is important for mapping
        # sub class lists and resolving mappings.
        base_parents = [p for p in parents if hasattr(p, '_subs')]
        for p in base_parents:
            if not issubclass(from_obj, p.from_obj):
                raise MappingSetupError('`from_obj` must be a subclass of `parent.from_obj`')
            if not issubclass(to_obj, p.to_obj):
                raise MappingSetupError('`to_obj` must be a subclass of `parent.to_obj`')

            # Copy mapping rules
            for mapping_rule in p._mapping_rules:
                mapping_rules.append(mapping_rule)
                remove_from_unmapped_fields(mapping_rule)

        # Add basic mappings
        for idx, mapping in enumerate(attrs.pop('mappings', [])):
            mapping_rule = attr_mapping_to_mapping_rule(mapping, 'basic mapping', idx)
            mapping_rules.append(mapping_rule)
            remove_from_unmapped_fields(mapping_rule)

        # Add custom mappings
        for attr in attrs.values():
            # Methods with a _mapping attribute have been decorated by either `map_field` or `map_list_field`
            # decorators.
            if hasattr(attr, '_mapping'):
                mapping_rule = attr_mapping_to_mapping_rule(getattr(attr, '_mapping'), 'custom mapping', attr)
                mapping_rules.append(mapping_rule)
                remove_from_unmapped_fields(mapping_rule)
                # Remove mapping
                delattr(attr, '_mapping')

        # Add auto mapped fields that are yet to be mapped.
        for field in unmapped_fields:
            if field in to_fields:
                mapping_rules.append(mcs.generate_auto_mapping(field, from_fields, to_fields))

        # Update attributes
        attrs['_mapping_rules'] = mapping_rules
        attrs['_subs'] = {}

        # Create mapper instance
        mapper = super_new(mcs, name, bases, attrs)
        if register_mapping:
            registration.register_mapping(mapper)
            mapper = registration.get_mapping(from_obj, to_obj)

            # Register mapping with parents mapping objects as a sub class.
            for parent in base_parents:
                parent._subs[from_obj] = mapper

        return mapper