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
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
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)
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
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)
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)
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)