A RESTAPI¶
Since version 19.5.10 pyDAL includes a restful API called RestAPI. It is inspired by GraphQL but it’s not quite the same because it is less powerful but, in the spirit of py4web, more practical and easier to use. Like GraphQL RestAPI allows a client to query for information using the GET method and allows to specify some details about the format of the response (which references to follow, and how to denormalize the data). Unlike GraphQL it allows the server to specify a policy and restrict which queries are allowed and which one are not. They can be evaluated dynamically per request based on the user and the state of the server. As the name implied RestAPI allows all stardard methods GET, POST, PUT, and DELETE. Each of them can be enabled or disabled based on the policy, for individual tables and individual fields.
Nota
Specifications might be subject to changes since this is a new feature.
Nos exemplos abaixo assumimos um aplicativo chamado “super-heróis” e o seguinte modelo:
db.define_table(
'person',
Field('name'),
Field('job'))
db.define_table(
'superhero',
Field('name'),
Field('real_identity', 'reference person'))
db.define_table(
'superpower',
Field('description'))
db.define_table(
'tag',
Field('superhero', 'reference superhero'),
Field('superpower', 'reference superpower'),
Field('strength', 'integer'))
We also assume the following controller:
from py4web import action, request
from .common import db
from pydal.restapi import RestAPI, Policy
policy = Policy()
policy.set('superhero', 'GET', authorize=True, allowed_patterns=['*'])
policy.set('*', 'GET', authorize=True, allowed_patterns=['*'])
# for security reasons we disabled here all methods but GET at the policy level, to enable any of them just set authorize = True
policy.set('*', 'PUT', authorize=False)
policy.set('*', 'POST', authorize=False)
policy.set('*', 'DELETE', authorize=False)
@action('api/<tablename>/', method = ['GET', 'POST'])
@action('api/<tablename>/<rec_id>', method = ['GET', 'PUT', 'DELETE'])
@action.uses(db)
def api(tablename, rec_id=None):
return RestAPI(db, policy)(request.method,
tablename,
rec_id,
request.GET,
request.POST
)
A política é por tabela (ou * para todas as tabelas e por método. Autorizar pode ser verdade (permitir), False (negar) ou uma função com a assinatura (método, tablename, record_id, get_vars, post_vars) que retorna Verdadeiro / Falso . para a política GET pode especificar uma lista de padrões de consulta permitidos (* para todos). um padrão de consulta será comparado com as chaves na cadeia de consulta.
A acção acima referida é exposto como:
/superheroes/api/{tablename}
/superheroes/api/{tablename}/{rec_id}
Nota
Keep in mind that request.POST only contains the form data
that is posted using a regular HTML-form or JavaScript
FormData object. If you post just plain object
(e.g. axios.post('path/to/api', {field:'some'})
) you should pass
request.json instead of request.POST, since the latter will contain
just raw request-body which is string, not JSON. See Bottle documentation about request
object.
RestAPI GET¶
A consulta geral tem a forma `` {} algo .eq = value`` onde `` eq = `` significa “igual”, `` gt = `` significa “maior que”, etc. A expressão pode ser prefixado por `` not.``.
`` {Algo} `` pode ser o nome de um campo na tabela foi consultado como em:
** Todos os super-heróis chamado de “Superman” **
/superheroes/api/superhero?name.eq=Superman
It can be the name of a field of a table referred by the table been queried as in:
** Todos os super-heróis com a identidade real “Clark Kent” **
/superheroes/api/superhero?real_identity.name.eq=Clark Kent
Pode ser o nome de um campo de uma tabela que se refere ao neen tabela consultada como em:
** Todos os super-heróis com qualquer superpotência tag com força> 90 **
/superheroes/api/superhero?superhero.tag.strength.gt=90
(here tag
is the name of the link table, the preceding superhero
is
the name of the field that refers to the selected table and strength
is the name of the field used to filter)
Ele também pode ser um campo da tabela referenciada por uma tabela de muitos-para-muitos ligado como em:
** Todos os super-heróis com o poder de vôo **
/superheroes/api/superhero?superhero.tag.superpower.description.eq=Flight
A chave para entender a sintaxe acima é para quebrá-lo da seguinte forma:
superhero?superhero.tag.superpower.description.eq=Flight
e lê-lo como:
selecionar registros da tabela ** super-herói ** referido pelo campo ** super-herói ** da tabela ** tag ** quando a superpotência ** ** campo dos ditos pontos de mesa para um recorde com a descrição ** ** ** ** eq ual para “Flight”.
A consulta permite modificadores adicionais, por exemplo
@offset=10
@limit=10
@order=name
@model=true
@lookup=real_identity
The first 3 are obvious. @model
returns a JSON description of database
model. @lookup
denormalizes the linked field.
Aqui estão alguns exemplos práticos:
URL:
/superheroes/api/superhero
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.132635",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@model=true
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"id": 3
}
],
"timestamp": "2021-01-04T07:03:38.466030",
"model": [
{
"regex": "[1-9]\\d*",
"name": "id",
"default": null,
"required": false,
"label": "Id",
"post_writable": true,
"referenced_by": [
"tag.superhero"
],
"unique": false,
"type": "id",
"options": null,
"put_writable": true
},
{
"regex": null,
"name": "name",
"default": null,
"required": false,
"label": "Name",
"post_writable": true,
"unique": false,
"type": "string",
"options": null,
"put_writable": true
},
{
"regex": null,
"name": "real_identity",
"default": null,
"required": false,
"label": "Real Identity",
"post_writable": true,
"references": "person",
"unique": false,
"type": "reference",
"options": null,
"put_writable": true
}
],
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@lookup=real_identity
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": {
"name": "Clark Kent",
"job": "Journalist",
"id": 1
},
"name": "Superman",
"id": 1
},
{
"real_identity": {
"name": "Peter Park",
"job": "Photographer",
"id": 2
},
"name": "Spiderman",
"id": 2
},
{
"real_identity": {
"name": "Bruce Wayne",
"job": "CEO",
"id": 3
},
"name": "Batman",
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.178974",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@lookup=identity:real_identity
(Desnormalizar o real_identity e renomeá-lo de identidade)
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1,
"identity": {
"name": "Clark Kent",
"job": "Journalist",
"id": 1
}
},
{
"real_identity": 2,
"name": "Spiderman",
"id": 2,
"identity": {
"name": "Peter Park",
"job": "Photographer",
"id": 2
}
},
{
"real_identity": 3,
"name": "Batman",
"id": 3,
"identity": {
"name": "Bruce Wayne",
"job": "CEO",
"id": 3
}
}
],
"timestamp": "2019-05-19T05:38:00.123218",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@lookup=identity!:real_identity[name,job]
(Desnormalizar o real_identity [mas apenas campos nome e trabalho], recolher a com o prefixo de identidade)
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"name": "Superman",
"identity.job": "Journalist",
"identity.name": "Clark Kent",
"id": 1
},
{
"name": "Spiderman",
"identity.job": "Photographer",
"identity.name": "Peter Park",
"id": 2
},
{
"name": "Batman",
"identity.job": "CEO",
"identity.name": "Bruce Wayne",
"id": 3
}
],
"timestamp": "2021-01-04T07:03:38.559918",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@lookup=superhero.tag
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"superhero.tag": [
{
"strength": 100,
"superhero": 1,
"id": 1,
"superpower": 1
},
{
"strength": 100,
"superhero": 1,
"id": 2,
"superpower": 2
},
{
"strength": 100,
"superhero": 1,
"id": 3,
"superpower": 3
},
{
"strength": 100,
"superhero": 1,
"id": 4,
"superpower": 4
}
],
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"superhero.tag": [
{
"strength": 50,
"superhero": 2,
"id": 5,
"superpower": 2
},
{
"strength": 75,
"superhero": 2,
"id": 6,
"superpower": 3
},
{
"strength": 10,
"superhero": 2,
"id": 7,
"superpower": 4
}
],
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"superhero.tag": [
{
"strength": 80,
"superhero": 3,
"id": 8,
"superpower": 2
},
{
"strength": 20,
"superhero": 3,
"id": 9,
"superpower": 3
},
{
"strength": 70,
"superhero": 3,
"id": 10,
"superpower": 4
}
],
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.201988",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?@lookup=superhero.tag.superpower
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"superhero.tag.superpower": [
{
"strength": 100,
"superhero": 1,
"id": 1,
"superpower": {
"id": 1,
"description": "Flight"
}
},
{
"strength": 100,
"superhero": 1,
"id": 2,
"superpower": {
"id": 2,
"description": "Strength"
}
},
{
"strength": 100,
"superhero": 1,
"id": 3,
"superpower": {
"id": 3,
"description": "Speed"
}
},
{
"strength": 100,
"superhero": 1,
"id": 4,
"superpower": {
"id": 4,
"description": "Durability"
}
}
],
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"superhero.tag.superpower": [
{
"strength": 50,
"superhero": 2,
"id": 5,
"superpower": {
"id": 2,
"description": "Strength"
}
},
{
"strength": 75,
"superhero": 2,
"id": 6,
"superpower": {
"id": 3,
"description": "Speed"
}
},
{
"strength": 10,
"superhero": 2,
"id": 7,
"superpower": {
"id": 4,
"description": "Durability"
}
}
],
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"superhero.tag.superpower": [
{
"strength": 80,
"superhero": 3,
"id": 8,
"superpower": {
"id": 2,
"description": "Strength"
}
},
{
"strength": 20,
"superhero": 3,
"id": 9,
"superpower": {
"id": 3,
"description": "Speed"
}
},
{
"strength": 70,
"superhero": 3,
"id": 10,
"superpower": {
"id": 4,
"description": "Durability"
}
}
],
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.322494",
"api_version": "0.1"
}
URL (que é uma linha única, dividida para facilitar a leitura):
/superheroes/api/superhero?
@lookup=powers:superhero.tag[strength].superpower[description]
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"powers": [
{
"strength": 100,
"superpower": {
"description": "Flight"
}
},
{
"strength": 100,
"superpower": {
"description": "Strength"
}
},
{
"strength": 100,
"superpower": {
"description": "Speed"
}
},
{
"strength": 100,
"superpower": {
"description": "Durability"
}
}
],
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"powers": [
{
"strength": 50,
"superpower": {
"description": "Strength"
}
},
{
"strength": 75,
"superpower": {
"description": "Speed"
}
},
{
"strength": 10,
"superpower": {
"description": "Durability"
}
}
],
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"powers": [
{
"strength": 80,
"superpower": {
"description": "Strength"
}
},
{
"strength": 20,
"superpower": {
"description": "Speed"
}
},
{
"strength": 70,
"superpower": {
"description": "Durability"
}
}
],
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.309903",
"api_version": "0.1"
}
URL (que é uma linha única, dividida para facilitar a leitura):
/superheroes/api/superhero?
@lookup=powers!:superhero.tag[strength].superpower[description]
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"powers": [
{
"strength": 100,
"description": "Flight"
},
{
"strength": 100,
"description": "Strength"
},
{
"strength": 100,
"description": "Speed"
},
{
"strength": 100,
"description": "Durability"
}
],
"id": 1
},
{
"real_identity": 2,
"name": "Spiderman",
"powers": [
{
"strength": 50,
"description": "Strength"
},
{
"strength": 75,
"description": "Speed"
},
{
"strength": 10,
"description": "Durability"
}
],
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"powers": [
{
"strength": 80,
"description": "Strength"
},
{
"strength": 20,
"description": "Speed"
},
{
"strength": 70,
"description": "Durability"
}
],
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.355181",
"api_version": "0.1"
}
URL (que é uma linha única, dividida para facilitar a leitura):
/superheroes/api/superhero?
@lookup=powers!:superhero.tag[strength].superpower[description],
identity!:real_identity[name]
RESULTADO:
{
"count": 3,
"status": "success",
"code": 200,
"items": [
{
"name": "Superman",
"identity.name": "Clark Kent",
"powers": [
{
"strength": 100,
"description": "Flight"
},
{
"strength": 100,
"description": "Strength"
},
{
"strength": 100,
"description": "Speed"
},
{
"strength": 100,
"description": "Durability"
}
],
"id": 1
},
{
"name": "Spiderman",
"identity.name": "Peter Park",
"powers": [
{
"strength": 50,
"description": "Strength"
},
{
"strength": 75,
"description": "Speed"
},
{
"strength": 10,
"description": "Durability"
}
],
"id": 2
},
{
"name": "Batman",
"identity.name": "Bruce Wayne",
"powers": [
{
"strength": 80,
"description": "Strength"
},
{
"strength": 20,
"description": "Speed"
},
{
"strength": 70,
"description": "Durability"
}
],
"id": 3
}
],
"timestamp": "2021-01-04T07:31:34.974953",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?name.eq=Superman
RESULTADO:
{
"count": 1,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1
}
],
"timestamp": "2019-05-19T05:38:00.405515",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?real_identity.name.eq=Clark Kent
RESULTADO:
{
"count": 1,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1
}
],
"timestamp": "2019-05-19T05:38:00.366288",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?not.real_identity.name.eq=Clark Kent
RESULTADO:
{
"count": 2,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 2,
"name": "Spiderman",
"id": 2
},
{
"real_identity": 3,
"name": "Batman",
"id": 3
}
],
"timestamp": "2019-05-19T05:38:00.451907",
"api_version": "0.1"
}
URL:
/superheroes/api/superhero?superhero.tag.superpower.description=Flight
RESULTADO:
{
"count": 1,
"status": "success",
"code": 200,
"items": [
{
"real_identity": 1,
"name": "Superman",
"id": 1
}
],
"timestamp": "2019-05-19T05:38:00.453020",
"api_version": "0.1"
}
All RestAPI response have the fields:
- api_version
RestAPI version.
- timestamp
Datetime in ISO 8601 format.
- status
RestAPI status (i.e. «success» or «error»).
- code
HTTP status.
Other optional fields are:
- count
Total matching (not total returned), for GET.
- items
In response to a GET.
- errors
Usually a validation error.
- models
Usually if status != «success».
- message
For error details.