namespace

Model

  • 参考:

  • parameters 字段

    Parameters: default – The default value for the field, if no value is specified. attribute – If the public facing value differs from the internal value, use this to retrieve a different attribute from the response than the publicly named value. title (str) – The field title (for documentation purpose) description (str) – The field description (for documentation purpose) required (bool) – Is the field required ? readonly (bool) – Is the field read only ? (for documentation purpose) example – An optional data example (for documentation purpose) mask (callable) – An optional mask function to be applied to output

  • 调用

    1
    2
    3
    4
    
    todo = api.model('Todo', {
        'id': fields.Integer(readonly=True, description='The task unique identifier'),
        'task': fields.String(required=True, description='The task details')
    })

api.model

  • 作用

    • 工厂函数
  • 完成功能

    • 自动把生成的 Model 注册和缓存到 api.models (一个 dict) 中

api.clone

  • 复制 给定 model 的 属性,注册新生成的 Model 到 api.models

api.inherit

序列化

marshal

  • 作用对象:dict
  • 根据模板序列化

    • 先过滤,再序列化
    • 类型不符合 –> 报错
  • 调用

    1
    
    marshal(data, fields, ...)
  • 例子

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    from flask_restx import fields, marshal
    
    marshal({'name': 'lucy', 'pro': 'teacher', 'score': 80},
            {'name': fields.String, 'pro': fields.String})
    # 正常
    # 输出
    {'name': 'lucy', 'pro': 'teacher'}
    
    
    # * 错误,类型不符
    marshal({'name': 'lucy', 'pro': 'teacher', 'score': 80},
            {'name': fields.String, 'pro': fields.Float})
    # --> pro: fields.Float
    # ==> 报错 MarshallingError

marshell_with

  • 特殊参数

    • as_list

      • 声明返回值是 List
      • 这是 fields 指定的是 元素的类型
  • 调用

    1
    2
    3
    
     @marshal_with(fields)
     def your_method():
         ...

marshal_with_field

  • 用法:类似 typing, 修饰返回值
  • 调用

    1
    2
    3
    4
    5
    6
    7
    
    >>> from flask_restx import marshal_with_field, fields
    >>> @marshal_with_field(fields.List(fields.Integer))
    ... def get():
    ...     return ['1', 2, 3.0]
    ...
    >>> get()
    [1, 2, 3]

marshal_list_with

  • 即 api.marshal_with(fileds, …, as_list=True)

Swagger Documentation

api.doc

  • 提供额外信息

response 类型解说

api.marshal_with

api.marshal_list_with

request 类型解说

api.expect

1
2
3
4
5
6
7
8
9
resource_fields = api.model('Resource', {
    'name': fields.String,
})

@api.route('/my-resource/<id>')
class MyResource(Resource):
    @api.expect([resource_fields])
    def get(self):
    pass
  • 解说

    • 只需 把 fields 写成 [fields] 即可

fields

类型

  • fields.String
  • fields.Url
  • fileds.Boolean
  • fileds.Integer
  • fileds.Float
  • fileds.Arbitray

    • 任意精度 float
  • List

Mask

  • 作用

    • 只选用 给定 Model 中的有限个 fields
  • 参考:Fields masks — Flask-RESTX 0.4.1.dev documentation
  • 调用

    1
    2
    3
    4
    5
    
    model = api.model('Person', {
    'name': fields.String,
    'age': fields.Integer,
    'boolean': fields.Boolean,
    }, mask='{name,age}')
    • 这里 只选用 name 和 age 字段
  • 嵌套字段的 mask

    1
    
      mask = '{name, age, pet{name}}'
    • 解释

      • json 有字段 name, age, pet
      • pet 字段有 pet 属性
  • 内层字段制定

    1
    
      mask = '{pets{name},*}'
    • 解释

      • 只有 pets 内部属性,只选用 name 属性
      • 其他外层字段,无过滤,全部选用

实例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
# -*- coding: utf-8; -*-
import json
import logging
import logging.config
from flask import Flask, request, send_from_directory
from flask_restx import Api, Resource, fields
from werkzeug.exceptions import HTTPException

import settings
settings.WEB_FRAMEWORK['framework'] = 'flask'
import web.errors
import web.utils
from chempeep import ChemPeep

logging.config.dictConfig(settings.LOG_CONFIG_DICT)

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
api = Api(app)


ner_requset_model = api.model('NER_Request', {
'tei_id': fields.Integer(description='tei xml id in database'),
'sentence': fields.String(description='Text of the given sentence')
})

# * 公共部分,多个层次需要
basic_response_model = api.model('Basic_Response', {
'code': fields.Integer(description='error code number'),
'msg': fields.String(description='error message string'),
'timestamp': fields.String(description='when the task or response generated.')
})

# * 最内层 json
variant_model = api.model('Chemical_Variant', {
'variant': fields.String(description='raw text from original input string'),
'start': fields.Integer(description='start index'),
'end': fields.Integer(description='end index(not included)'),
'extract_tool': fields.String(description='been extracted with which NER Tool')
})

chemical_model = api.model('Chemical', {
'chemical': fields.String(description='chemical text, possibly was drop whitespace with chempattern'),
'inchi': fields.String(description='inchi of the chemical'),
'smiles': fields.String(description='smiles of the chemical'),

# ---------------------------------------
# * 内嵌 `Model` 列表
'source_variants': fields.List(fields.Nested(variant_model), description='a list of varaint text in input string'),
})

# sentence_model = api.model('Sentence', {
sentence_model = api.clone('Sentence', basic_response_model, {
'tei_id': fields.Integer(),
'original_text': fields.String(),
'rendered_text': fields.String(description='text, after dropped whitespace'),
'chemical_list': fields.List(fields.Nested(chemical_model), description='chemicals extracted'),

})

ner_response_model = api.clone('NER_Response', basic_response_model, {
'success_count': fields.Integer(description='How many tasks successfull rendered.'),
'error_count': fields.Integer(description='How many task failed.'),
'rendered_tasks': fields.List(fields.Nested(sentence_model))
})

@api.route('/chemical/ner', methods=['GET', 'POST'])
class ChemicalNER(Resource):
def get(self):
    return {
    "code": 200,
    "msg": "Route to crop images for one pdf file"
    }

@api.expect([ner_requset_model])
@api.marshal_with(ner_response_model)
# @api.make_response(200, 'Success', ner_response_model)
def post(self):
    whole_json = request.get_json()
    # * no json
    if whole_json is None:
    raise web.errors.WithoutJsonError('No json provided in request.')

    app.logger.info('request_json: %s', json.dumps(whole_json, ensure_ascii=False))

    result = web.utils.ChemNERRender(whole_json).render()

    app.logger.info("rendered result: %s", json.dumps(result, ensure_ascii=False))
    return result


@api.errorhandler(web.errors.MatgeneAPIException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
app.logger.exception(e)
return e.get_response()

@api.errorhandler(HTTPException)
def handle_default_exception(e):
if isinstance(e, HTTPException):
    return web.errors.MatgeneAPIException(e.code, e.description).get_response()

app.logger.exception(e)
new_e = web.errors.MatgeneAPIException(500, 'Unknow Internal Exception')
return new_e.get_response()

if __name__ == '__main__':
# app.run(port=9091, host='0.0.0.0', debug=False)
   app.run(port=settings.REST_API_PORT, host='0.0.0.0', debug=True)