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 []
Beispiel #2
0
    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)
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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)
Beispiel #6
0
    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'])
Beispiel #7
0
    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
Beispiel #8
0
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)
Beispiel #9
0
    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__
Beispiel #10
0
    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
Beispiel #11
0
    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_
Beispiel #12
0
    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_
Beispiel #13
0
    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__
Beispiel #14
0
    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
Beispiel #15
0
    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 []
Beispiel #16
0
    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_
Beispiel #17
0
    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