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 SPLIT_REL not in field: 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 """ if obj is None: url_field = getattr(self, "url_field", "id") filter_value = view_kwargs[url_field] raise ObjectNotFound( f"{self.model.__name__}: {filter_value} not found", source={"parameter": url_field}) self.before_update_object(obj, 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 for i_plugins in self.resource.plugins: try: data = i_plugins.data_layer_update_object_clean_data( data=data, obj=obj, view_kwargs=view_kwargs, join_fields=join_fields, self_json_api=self, ) except PluginMethodNotImplementedError: pass for key, value in data.items(): if hasattr(obj, key) and key not in join_fields: setattr(obj, key, value) self.apply_relationships(data, obj) self.apply_nested_fields(data, obj) try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() orig_e = getattr(e, "orig", object) message = getattr(orig_e, "args", []) message = message[0] if message else None e = message if message else e raise JsonApiException("Update object error: " + str(e), source={"pointer": "/data"}) self.after_update_object(obj, data, view_kwargs)
def _get_relationship_data(self): """Get useful data for relationship management""" relationship_field = request.path.split("/")[-1].replace("-", "_") if relationship_field not in get_relationships(self.schema): raise RelationNotFound(f"{self.schema.__name__} has no attribute {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_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 attribute {}".format( self.schema.__name__, relationship_field)) return self.schema._declared_fields[ relationship_field].schema.__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 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 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 """ for i_plugins in self.resource.plugins: try: i_plugins.data_layer_before_create_object( data=data, view_kwargs=view_kwargs, self_json_api=self) except PluginMethodNotImplementedError: pass 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 for i_plugins in self.resource.plugins: try: data = i_plugins.data_layer_create_object_clean_data( data=data, view_kwargs=view_kwargs, join_fields=join_fields, self_json_api=self, ) except PluginMethodNotImplementedError: pass 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) for i_plugins in self.resource.plugins: try: i_plugins.data_layer_after_create_object( data=data, view_kwargs=view_kwargs, obj=obj, self_json_api=self, ) except PluginMethodNotImplementedError: pass self.session.add(obj) try: self.session.commit() except JsonApiException as e: self.session.rollback() raise e except Exception as e: self.session.rollback() raise JsonApiException(f"Object creation error: {e}", source={"pointer": "/data"}) self.after_create_object(obj, data, view_kwargs) return obj