def sorting(self): """Return fields to sort by including sort name for SQLAlchemy and row sort parameter for other ORMs :return list: a list of sorting information Example of return value:: [ {'field': 'created_at', 'order': 'desc'}, ] """ if self.qs.get('sort'): sorting_results = [] for sort_field in self.qs['sort'].split(','): field = sort_field.replace('-', '') if field not in self.schema._declared_fields: raise InvalidSort("{} has no attribute {}".format( self.schema.__name__, field)) if field in get_relationships(self.schema): raise InvalidSort( "You can't sort on {} because it is a relationship field" .format(field)) field = get_model_field(self.schema, field) order = 'desc' if sort_field.startswith('-') else 'asc' sorting_results.append({'field': field, 'order': order}) return sorting_results return []
def update_object(self, obj, data, view_kwargs): """Update an object through sqlalchemy :param DeclarativeMeta obj: an object from sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return boolean: True if object have changed else False """ self.before_update_object(obj, data, view_kwargs) relationship_fields = get_relationships(self.resource.schema) for key, value in data.items(): if hasattr(obj, key) and key not in relationship_fields: setattr(obj, key, value) self.apply_relationships(data, obj) try: self.session.commit() except Exception as e: self.session.rollback() raise JsonApiException({'pointer': '/data'}, "Update object error: " + str(e)) self.after_update_object(obj, data, view_kwargs)
def create_object(self, data, view_kwargs): """Create an object through sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return DeclarativeMeta: an object from sqlalchemy """ self.before_create_object(data, view_kwargs) relationship_fields = get_relationships(self.resource.schema, model_field=True) obj = self.model( **{ key: value for (key, value) in data.items() if key not in relationship_fields }) self.apply_relationships(data, obj) self.session.add(obj) try: self.session.commit() except Exception as e: self.session.rollback() raise JsonApiException("Object creation error: " + str(e), source={'pointer': '/data'}) self.after_create_object(obj, data, view_kwargs) return obj
def create_object(self, data, view_kwargs): """Create an object through sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return DeclarativeMeta: an object from sqlalchemy """ self.before_create_object(data, view_kwargs) relationship_fields = get_relationships(self.resource.schema, model_field=True) nested_fields = get_nested_fields(self.resource.schema, model_field=True) join_fields = relationship_fields + nested_fields obj = self.model(**{key: value for (key, value) in data.items() if key not in join_fields}) self.apply_relationships(data, obj) self.apply_nested_fields(data, obj) self.session.add(obj) try: self.session.commit() except JsonApiException as e: try: self.session.rollback() except Exception as e: print 'Rollback failed {}'.format(e) raise e self.after_create_object(obj, data, view_kwargs) return obj
def update_object(self, obj, data, view_kwargs): """Update an object through sqlalchemy :param DeclarativeMeta obj: an object from sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return boolean: True if object have changed else False """ if obj is None: url_field = getattr(self, 'url_field', 'id') filter_value = view_kwargs[url_field] raise ObjectNotFound('{}: {} not found'.format( self.model.__name__, filter_value), source={'parameter': url_field}) self.before_update_object(obj, data, view_kwargs) relationship_fields = get_relationships(self.resource.schema, model_field=True) for key, value in data.items(): if hasattr(obj, key) and key not in relationship_fields: setattr(obj, key, value) self.apply_relationships(data, obj) try: self.session.commit() except Exception as e: self.session.rollback() raise JsonApiException("Update object error: " + str(e), source={'pointer': '/data'}) self.after_update_object(obj, data, view_kwargs)
def apply_relationships(self, data, obj): """Apply relationship provided by data to obj :param dict data: data provided by the client :param DeclarativeMeta obj: the sqlalchemy object to plug relationships to :return boolean: True if relationship have changed else False """ relationships_to_apply = [] relationship_fields = get_relationships(self.resource.schema, model_field=True) for key, value in data.items(): if key in relationship_fields: related_model = getattr(obj.__class__, key).property.mapper.class_ schema_field = get_schema_field(self.resource.schema, key) related_id_field = self.resource.schema._declared_fields[schema_field].id_field if isinstance(value, list): related_objects = [] for identifier in value: related_object = self.get_related_object(related_model, related_id_field, {'id': identifier}) related_objects.append(related_object) relationships_to_apply.append({'field': key, 'value': related_objects}) else: related_object = None if value is not None: related_object = self.get_related_object(related_model, related_id_field, {'id': value}) relationships_to_apply.append({'field': key, 'value': related_object}) for relationship in relationships_to_apply: setattr(obj, relationship['field'], relationship['value'])
def update_object(self, obj, data, **view_kwargs): """Update an object through sqlalchemy :param DeclarativeMeta obj: an object from sqlalchemy :param dict data: the data validated by marshmallow :param dict view_kwargs: kwargs from the resource view :return boolean: True if object have changed else False """ self.before_update_object(obj, data, **view_kwargs) update = False relationship_fields = get_relationships(self.resource.schema) for field in data: if hasattr(obj, field) and field not in relationship_fields: if getattr(obj, field) != data[field]: update = True setattr(obj, field, data[field]) update_relationship = self.apply_relationships(data, obj) if update_relationship is True: update = True try: self.session.commit() except Exception as e: self.session.rollback() raise JsonApiException({'pointer': '/data'}, "Update object error: " + str(e)) return update
def validate_custom_form_constraints_request(form, schema, obj, data): new_obj = type(obj)(**object_as_dict(obj)) relationship_fields = get_relationships(schema) for key, value in data.items(): if hasattr(new_obj, key) and key not in relationship_fields: setattr(new_obj, key, value) return validate_custom_form_constraints(form, new_obj)
def related_schema(self): """Get the related schema of a relationship field :return Schema: the related schema """ relationship_field = self.name if relationship_field not in get_relationships(self.schema): raise InvalidFilters("{} has no relationship attribut {}".format(self.schema.__name__, relationship_field)) return self.schema._declared_fields[relationship_field].schema.__class__
def _get_relationship_data(self): """Get useful data for relationship management""" relationship_field = request.path.split('/')[-1] if relationship_field not in get_relationships(self.schema): raise RelationNotFound("{} has no attribute {}".format(self.schema.__name__, relationship_field)) related_type_ = self.schema._declared_fields[relationship_field].type_ related_id_field = self.schema._declared_fields[relationship_field].id_field model_relationship_field = get_model_field(self.schema, relationship_field) return relationship_field, model_relationship_field, related_type_, related_id_field
def related_model(self): """Get the related model of a relationship field :return DeclarativeMeta: the related model """ relationship_field = self.name if relationship_field not in get_relationships(self.schema): raise InvalidFilters("{} has no relationship attribut {}".format(self.schema.__name__, relationship_field)) if hasattr(self.opts, 'schema_to_model') and self.opts.schema_to_model.get(relationship_field) is not None: relationship_field = self.opts.schema_to_model[relationship_field] return getattr(self.model, relationship_field).property.mapper.class_
def related_model(self): """Get the related model of a relationship field :return DeclarativeMeta: the related model """ relationship_field = self.name if relationship_field not in get_relationships(self.schema): raise InvalidFilters("{} has no relationship attribute {}".format( self.schema.__name__, relationship_field)) return getattr(self.model, get_model_field( self.schema, relationship_field)).property.mapper.class_
def related_schema(self): """Get the related schema of a related (relationship or nested) field :return Schema: the related schema """ related_field_name = self.name related_fields = get_relationships(self.schema) + get_nested_fields( self.schema) if related_field_name not in related_fields: raise InvalidFilters( "{} has no relationship or nested attribute {}".format( self.schema.__name__, related_field_name)) return self.schema._declared_fields[ related_field_name].schema.__class__
def _get_relationship_data(self): """Get useful data for relationship management """ relationship_field = request.base_url.split('/')[-1] if relationship_field not in get_relationships(self.schema): raise RelationNotFound('', "{} has no attribut {}".format(self.schema.__name__, relationship_field)) related_type_ = self.schema._declared_fields[relationship_field].type_ related_id_field = self.schema._declared_fields[relationship_field].id_field if hasattr(self.opts, 'schema_to_model') and\ self.opts.schema_to_model.get(relationship_field) is not None: relationship_field = self.opts.schema_to_model[relationship_field] return relationship_field, related_type_, related_id_field
def sorting(self): """Return fields to sort by including sort name for SQLAlchemy and row sort parameter for other ORMs :return list: a list of sorting information Example of return value:: [ {'field': 'created_at', 'order': 'desc'}, ] """ if self.qs.get('sort'): sorting_results = [] for sort_field in self.qs['sort'].split(','): field = sort_field.replace('-', '') order = 'desc' if sort_field.startswith('-') else 'asc' if '.' in field: # attempting to sort on relationship relationship_path = '.'.join(field.split('.')[0:-1]) target_attribute = field.split('.')[-1] relationship_schema = get_related_schema_path( self.schema, relationship_path) if not get_model_field(relationship_schema, target_attribute): raise InvalidSort("Invalid sort path {}".format(field)) sorting_results.append({ 'field': target_attribute, 'order': order, 'relationship': relationship_path }) elif field not in self.schema._declared_fields: raise InvalidSort("{} has no attribute {}".format( self.schema.__name__, field)) elif field in get_relationships(self.schema): raise InvalidSort( "You can't sort on {} because it is a relationship field" .format(field)) else: field = get_model_field(self.schema, field) sorting_results.append({'field': field, 'order': order}) return sorting_results return []
def related_model(self): """Get the related model of a related (relationship or nested) field :return DeclarativeMeta: the related model """ related_field_name = self.name related_fields = get_relationships(self.schema) + get_nested_fields( self.schema) if related_field_name not in related_fields: raise InvalidFilters( "{} has no relationship or nested attribute {}".format( self.schema.__name__, related_field_name)) return getattr(self.model, get_model_field( self.schema, related_field_name)).property.mapper.class_
def apply_relationships(self, data, obj): """Apply relationship provided by data to obj :param dict data: data provided by the client :param DeclarativeMeta obj: the sqlalchemy object to plug relationships to :return boolean: True if relationship have changed else False """ updated = False relationship_fields = get_relationships(self.resource.schema) for key, value in data.items(): if key in relationship_fields: relationship_field = key related_id_field = self.resource.schema._declared_fields[ relationship_field].id_field if hasattr(self.resource.opts, 'schema_to_model') and\ self.resource.opts.schema_to_model.get(key) is not None: relationship_field = self.resource.opts.schema_to_model[ relationship_field] related_model = getattr( obj.__class__, relationship_field).property.mapper.class_ if isinstance(data[key], list): related_objects = [] for identifier in data[key]: related_object = self.get_related_object( related_model, related_id_field, {'id': identifier}) related_objects.append(related_object) obj_ids = { getattr(obj__, related_id_field) for obj__ in getattr(obj, relationship_field) } new_obj_ids = { getattr(related_object, related_id_field) for related_object in related_objects } if obj_ids != new_obj_ids: updated = True setattr(obj, relationship_field, related_objects) else: related_object = None if data[key] is not None: related_object = self.get_related_object( related_model, related_id_field, {'id': data[key]}) obj_id = getattr(getattr(obj, relationship_field), related_id_field, None) new_obj_id = getattr(related_object, related_id_field, None) if obj_id != new_obj_id: updated = True setattr(obj, relationship_field, related_object) return updated