API¶
The platform consists of both backend and frontend. Each of those two parts doing own work - backend is a server-side, data-storing and computing set of processes. Frontend is an any application, including web browser tab with loaded Wefram-based web code, or mobile apps for Android, Apple iOS and etc.
The frontend itself costs nothing without working backend and a some posibility to interact with it. The API is the way the frontend and backend, in general, interacting with each other.
Entity API reference¶
- class wefram.api.EntityAPI(key: Optional[Any] = None)¶
The API-ready entity abstract class. Implements much of logics for the HTTP requests handling, endpointing to the EntityCRUD defined abstract methods.
- app: str¶
The application for which is entity is applicable to. Filled automatically at the entity registration time.
- paths: dict¶
The dict, containing registerd API paths for the every API method. For example:
{ 'create': '/api/<app>/<EntityName>', 'read': '/api/<app>/<EntityName>/<key>', 'list': '/api/<app>/<EntityName>', 'update': '/api/<app>/<EntityName>/<key>', 'update_batch': '/api/<app>/<EntityName>', 'options': '/api/<app>/<EntityName>', 'options_resolve': '/api/<app>/<EntityName>/resolve', 'delete': '/api/<app>/<EntityName>/<key>', 'delete_batch': '/api/<app>/<EntityName>', }
So, by accessing the entity class’es
path
property using the corresponding method key, the programmer may get the absolute URL for accessing the corresponding API method from the frontend.
- name: Optional[str] = None¶
The entity name. Filled automatically basing on the entity class name.
- version: Optional[Union[int, str]] = None¶
Optional, the API version. Gives an ability to handle different versions of the entity by declaring different entity classes (for example, basing on the one general class and hanlding differences between versions in the inheited ones.
- path_prefix: Optional[str] = None¶
Optional prefix used on the absolute routing path generation. This prefix, if specified, will be used in the routing path right after /api and before the rest of the path. For example:
prefix = 'myprefix'
The path will be:
/api/myprefix/myapp/v1/MyEntity
- path_suffix: Optional[str] = None¶
Optional suffix used on the absolute routing path generation. This suffix, if specified, will be used in the routing path at the end. For example:
suffix = 'mysuffix'
The path will be:
/api/myapp/v1/MyEntity/mysuffix
- requires: Optional[Union[str, List[str]]] = None¶
The general requirement for access rights. The programmer may list required permissions (scopes) here and them will be used by default for all API methods, except ones whose declares own requirements.
For example:
class MyEntity(EntityAPI): requires = ['mypermission']
- allow_create: Union[bool, List[str]] = True¶
If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).
class MyEntity1(EntityAPI): # Permit the method, but require 'some_access' permission requires = ['some_access'] allow_create = True # may be omitted because by default is True class MyEntity2(EntityAPI): # Permit the method & requires 'other_permission', and not requires # any other permissions to access. requires = ['some_access'] allow_create = ['other_permission']
- allow_read: Union[bool, List[str]] = True¶
If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).
class MyEntity1(EntityAPI): # Permit the method, but require 'some_access' permission requires = ['some_access'] allow_read = True # may be omitted because by default is True class MyEntity2(EntityAPI): # Permit the method & requires 'other_permission', and not requires # any other permissions to access. requires = ['some_access'] allow_read = ['other_permission']
- allow_options: Union[bool, List[str]] = True¶
If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).
class MyEntity1(EntityAPI): # Permit the method, but require 'some_access' permission requires = ['some_access'] allow_options = True # may be omitted because by default is True class MyEntity2(EntityAPI): # Permit the method & requires 'other_permission', and not requires # any other permissions to access. requires = ['some_access'] allow_options = ['other_permission']
- allow_update: Union[bool, List[str]] = True¶
If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).
class MyEntity1(EntityAPI): # Permit the method, but require 'some_access' permission requires = ['some_access'] allow_update = True # may be omitted because by default is True class MyEntity2(EntityAPI): # Permit the method & requires 'other_permission', and not requires # any other permissions to access. requires = ['some_access'] allow_update = ['other_permission']
- allow_delete: Union[bool, List[str]] = True¶
If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).
class MyEntity1(EntityAPI): # Permit the method, but require 'some_access' permission requires = ['some_access'] allow_delete = True # may be omitted because by default is True class MyEntity2(EntityAPI): # Permit the method & requires 'other_permission', and not requires # any other permissions to access. requires = ['some_access'] allow_delete = ['other_permission']
- classmethod path_base() str ¶
Internal method which returns a routing path base string basing (by default) on the entity class name. Useful for overriding how the entity sounds in the routing path (URL).
- static prepare_response(result: Any, status_code: int = 200) starlette.responses.Response ¶
Wrapper method which get the handler result and returns the HTTP response. This allows the handler methods (at CRUD model) to return Python-basic response like ‘True’ or dict or list, or even just a string, not dealing with HTTP response generation.
- Parameters
result – The result of the API handler, which may be any kind of type.
status_code – The status code which about to use as HTTP status code in the response; by default is ‘200 OK’;
- Returns
The formed and ready to send to the calling frontend client response.
- async classmethod parse_request_payload(request: starlette.requests.Request) Optional[Dict[str, Any]] ¶
Parses the payload of the request. If the payload is dict (most often case), then rename dict’s keys from localCamesCase (typical for the JSON, JS & TS) to the snake_case (instead, typical for the Python).
- classmethod parse_request_arguments(request: starlette.requests.Request) Dict[str, str] ¶
Parses the query arguments (if any) and returns them as dict. For the current moment - it is just a proxy function to the middleware prepared arguments’ dict.
- async classmethod handle_create(request: starlette.requests.Request) starlette.responses.Response ¶
Creates a single entity object, using JSON or FormData payload to set it up with the values. Returns no content response 201 if succeeded.
If argument :argument return_key: setted to ‘true’ - return the new entity object’s key (which requires FLUSH+COMMIT operation to be executed in the ORM prior to response been completed, usually).
- async classmethod handle_read(request: starlette.requests.Request) starlette.responses.Response ¶
The method both handling listing of entities, and returning a single entity data. If the :path_param key: is not None - the single entity object will be fetched and resulted as JSONed dict. If the :path_param key: is None - the list of entity objects will be fetched, corresponding with the given in args filters, and returned as JSONed list of objects’ dicts.
- async classmethod handle_options(request: starlette.requests.Request) starlette.responses.Response ¶
This method returns a dict, containing keys and corresponding object’s human-readable representations, like names.
- async classmethod handle_update(request: starlette.requests.Request) starlette.responses.Response ¶
This method both can update a single entity object, given using :path_param key: argument (in the URL), and a set of entity objects, given by keys[] as URL argument (used for batch update of several objects at a time).
- async classmethod handle_delete(request: starlette.requests.Request) starlette.responses.Response ¶
This method both can delete a single entity object, given using :path_param key: argument (in the URL), and a set of entity objects, given by keys[] as URL argument.
Model API reference¶
- class wefram.api.ModelAPI(key: Optional[Any] = None)¶
The special case of the EntityAPI class, implementing the ORM model CRUD operations. This simplificates the API creation for ORM models, realizes corresponding, routed handlers for common operations.
Notice that there is a good idea to override some methods on this basic model if there is a need to make more complex behaviour on the one or the another operation.
For example, let’s assume that we have a some extra operations which must be done AFTER the object been successfully deleted. Let’s make a simple example on that:
class MyModel(ModelAPI): model = mymodels.MyModel async def delete(self, *keys: Union[str, int]) -> None: await super().delete(*keys) # There are no exceptions raised, so we know that deletion # was successful, and do some extra work here. print("Funny, its works!")
- model: ClassVar[sqlalchemy.orm.decl_api.Base]¶
The corresponding ORM model class for which this API is declaring for.
- set_name: Optional[str] = None¶
Optional name of the
Model.Meta.attributes_sets
entry, which will be used for read operations.
- return_created_id: bool = False¶
If set to
True
- the create operation will return an object key to the frontend on object create done with HTTP result code 200; otherwise only Success code 204 (No response) will be returned, indicating the successful creation.This might be useful especially when using auto-increment keys and there is a need to know the created object’s key after it creation.
Default is
False
.
- default_deep: bool = False¶
The API has two behaviours on how to return objects’ structs - simple and deep. The difference is that in deep behaviour dependend objects (for example, joined using Many-to-One, Many-to-Many, One-to-Many, One-to-One relationships) will be fetched from the database too and their first-line representations will be JSONified and returned to the caller too. See
dict()
of thewefram.ds.model.Model
class for more details.This behaviour controlled by passing URL argument
deep=true
ordeep=false
(or even omitting this argument). So, when the argument is omitted, the default behaviour, which is controlled by this attribute, will be used.Default is
False
.
- classmethod path_base() str ¶
Overrides the default entity-API path base to the model original name (because the class name rewrites by the ORM mechanics).
- property key_clause: Optional[sqlalchemy.sql.elements.ClauseList]¶
Returns a ready to use in the query key clause for the corresponding, declared by
model
ORM model and this instance. Ready to be used in the WHERE clause by the ORM mechanics.
- keys_clause(keys: List[Union[str, int]]) Optional[sqlalchemy.sql.elements.ClauseElement] ¶
Returns a ready to use in the query key clause for the corresponding, declared by
model
ORM model and given list of SIMPLE keys. Ready to be used in the WHERE clause by the ORM mechanics.Please note that this method does not work with complex keys!
- Parameters
keys – A list of simple keys (str or int type).
- Returns
A ready to use in the query SQLAlchemy clause, based on IN database operator.
- async classmethod create(**with_values) Union[bool, object] ¶
Creates a new ORM model instance, filling up it with the given in the payload values, saving it to the database.
Note that this method calls
flush()
function of the database session to ensure successful operation and handle some special case errors like integrity errors.
- handle_read_filter(c: sqlalchemy.orm.attributes.QueryableAttribute, value: Any) Optional[sqlalchemy.sql.elements.ClauseElement] ¶
Used by the class mechanics to handle passed in the URL argument filter and return an SQLAlchemy ready to use clause.
- Parameters
c – The corresponding ORM Model attribute (column) for which to handle filter for;
value – The raw value which about to be handled.
- Returns
SQLAlchemy ready to use clause
- async filter_read(like: Optional[str] = None, ilike: Optional[str] = None, **filters: Any) sqlalchemy.sql.elements.ClauseList ¶
Returns ready to use SQLAlchemy filter clause basing on the given filters for the read CRUD operation.
There are two basic filtering handling by this method:
value filtering - filter model objects by some value (some defined model column);
- search - try to find objects by
search
operation (for more details see like()
andilike()
). Search operation may be, according to corresponding methods of theModel
class -like
(strict, case sensetive search) andilike
(case insensetive search).
- search - try to find objects by
Filters expected to be specified using URL arguments. For example:
[GET] /api/myapp/MyModel?name=John&lastName=Final
or
[GET] /api/myapp/MyModel?ilike=eugene
Note that standard Wefram frontend functionality takes care on how to make the resulting URL with or without filters, by using corresponding frontend API methods (TypeScript written).
- async item_as_json(instance: sqlalchemy.orm.decl_api.Base, deep: Optional[bool] = None) dict ¶
Returns the given instance JSONified.
- Parameters
instance (Model instance) – The ORM Model instance to JSONify to.
deep (bool) – Do the deep JSONify or not. See
dict()
of thewefram.ds.model.Model
class for more details
- Returns
The JSONify ready Python dict.
- Return type
dict
- async items_as_json(instances: List[sqlalchemy.orm.decl_api.Base], deep: Optional[bool] = None) List[dict] ¶
Returns the given instances JSONified.
- Parameters
instances (List of Model instances) – The ORM Model instances to JSONify to.
deep (bool) – Do the deep JSONify or not. See
dict()
of thewefram.ds.model.Model
class for more details
- Returns
The JSONify ready Python dict.
- Return type
List of dicts
- async read(*keys: Union[str, int], count: bool = False, offset: Optional[int] = None, limit: Optional[int] = None, order: Optional[Union[str, List[str]]] = None, deep: Optional[bool] = None, like: Optional[str] = None, ilike: Optional[str] = None, **filters: Any) Union[sqlalchemy.orm.decl_api.Base, dict, List[dict]] ¶
The CRUD’s READ operation, used both for listing of existing entity objects like a list, and fetching a single object details.
- async options(*keys: Union[str, int], like: Optional[str] = None, ilike: Optional[str] = None) dict ¶
About to return dict containing keys and corresponding human readable representations. For example, user’s keys and corresponding names.
- async update(*keys: Union[str, int], **values) None ¶
The CRUD’s UPDATE operation, used to update the existing entity object (or objects, if the :param keys: can be handled by the real method).
- async delete(*keys: Union[str, int]) None ¶
The CRUD’s DELETE operation, used to delete according entity object(s).