使用Flask-Restful生成带有fields.Url的URL会生成BuildError - python

我想改编出色的Tutorial from Miguel Grinberg来创建一个unittest测试执行程序。我主要是根据Miguel的代码进行调整,但是我在字段映射中生成uri时遇到了问题。只要我删除

'uri': fields.Url('test')

一切正常,但是否则我会收到一个构建错误:

BuildError: ('test', {'Test_environment_id': 123, 'Test_duration': '0.5 sec', 'Success': 1, 'Failure_count': 0, 'Tested_files': 'Some files', 'Request_id': 1, 'Runs_count': 3, 'Created_on': '01.01.1970', 'Error_count': 0, 'Requester': 'John', 'Skipped_count': 2}, None)

我在stackoverflow here上发现了非常类似的问题,但这并没有帮助我理解我的代码出了什么问题。我可以使用该问题中所述的解决方法,但我真的很想知道我在做什么错。

这是我的代码:

#!/usr/bin/python
__author__ = 'karlitos'

from flask import Flask, jsonify, abort, make_response, request
from flask.ext.restful import Api, Resource, reqparse, fields, marshal,url_for
from time import strftime
from glob import glob
import os
import sqlite3

CURRENT_DIRECTORY = os.getcwd()
PROJECT_DIRECTORIES = glob('{}/projects/*'.format(CURRENT_DIRECTORY))
# create a sqlite database connection object
db_con = sqlite3.connect('{}unittest.db'.format(CURRENT_DIRECTORY))

app = Flask(__name__, static_url_path="")
api = Api(app)

tests = [
    {
        'Request_id': 1,
        'Requester': 'John',
        'Created_on': '01.01.1970',
        'Test_environment_id': 123,
        'Tested_files': 'Some files',
        'Test_duration': '0.5 sec',
        'Runs_count': 3,
        'Error_count': 0,
        'Failure_count': 0,
        'Skipped_count': 2,
        'Success': 1
    }
]

"""Structure storing the `Request_id`'s of all test currently running indexed by their `Test_environment_id`'s."""
env_id_of_running_tests = {}

"""Structure serving as a template for the `marshal` function which takes raw data and a dict of fields to output and
 filters the data based on those fields."""
test_fields = {
    'Request_id': fields.Integer,
    'Requester': fields.String,
    'Created_on': fields.String,
    'Test_environment_id': fields.Integer,
    'Tested_files': fields.String,
    'Test_duration': fields.String,
    'Runs_count': fields.Integer,
    'Error_count': fields.Integer,
    'Failure_count': fields.Integer,
    'Skipped_count': fields.Integer,
    'Success': fields.Boolean,
    'uri': fields.Url('test')
}

"""Validation function for the environment-id type which has to be in range [1,100]"""


def env_id_type(value, name):
    if value <= 1 or value >= 100:
        raise ValueError("The parameter '{}' is not between 1 and 100. The value: {} was provided".format(name, value))
    return value


class TestsAPI(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('Requester', type=str, required=True,
                                   help='No requester name provided', location='json')
        self.reqparse.add_argument('Test_environment_id', type=env_id_type, required=True,
                                   help='Bad environment-id provided, between 1 and 100.', location='json')
        super(TestsAPI, self).__init__()

    def get(self):
        return {'tests': [marshal(test, test_fields) for test in tests]}

    def post(self):
        args = self.reqparse.parse_args()

        request_id = tests[-1]['Request_id'] + 1

        # check if the current Test_environment_id is not under the currently running test
        if args['Test_environment_id'] in env_id_of_running_tests:
            return {'message': 'Another test with the same Environment-ID is still running.'}, 409
        else:
            env_id_of_running_tests[args['Test_environment_id']] = request_id
        test = {
            'Request_id': request_id,
            'Requester': args['Requester'],
            'Created_on': strftime('%a, %d %b %Y %H:%M:%S'),
            'Test_environment_id': args['Test_environment_id'],
            'Tested_files': 'Some files',
            'Test_duration': '',
            'Runs_count': None,
            'Error_count': None,
            'Failure_count': None,
            'Skipped_count': None,
            'Success': None
        }

        tests.append(test)

        return {'test started': marshal(test, test_fields)}, 201


class TestAPI(Resource):

    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument('Request_id', type=int, required=True,
                                   help='No Request-ID provided', location='json')
        super(TestAPI, self).__init__()

    def get(self, request_id):
        test = [test for test in tests if test['Request_id'] == request_id]
        print 'Request_ID', request_id
        if len(test) == 0:
            abort(404)
        return {'test   ': marshal(test[0], test_fields)}


api.add_resource(TestsAPI, '/test-executor/api/tests', endpoint='tests')
api.add_resource(TestAPI, '/test-executor/api/tests/<int:request_id>', endpoint='test')

if __name__ == '__main__':
    app.run(debug=True)

参考方案

您的“测试”端点的路由具有参数request_id(带有小写的首字母r),但是您的测试数据字典具有带键Request_id(大写的首字母R)的条目。在封送数据时,flask-restful在测试数据字典中查找带有小写字母request_id的条目以构造URL,但由于大小写不匹配而无法找到它。

如果将路由中的参数更改为大写Request_id,它将处理BuildError来请求“测试”端点。但是向测试端点发出请求后,由于TestAPI.get()中的大小写不匹配,您将遇到新的易于修复的错误。

在返回'Response'(Python)中传递多个参数 - python

我在Angular工作,正在使用Http请求和响应。是否可以在“响应”中发送多个参数。角度文件:this.http.get("api/agent/applicationaware").subscribe((data:any)... python文件:def get(request): ... return Response(seriali…

Python exchangelib在子文件夹中读取邮件 - python

我想从Outlook邮箱的子文件夹中读取邮件。Inbox ├──myfolder 我可以使用account.inbox.all()阅读收件箱,但我想阅读myfolder中的邮件我尝试了此页面folder部分中的内容,但无法正确完成https://pypi.python.org/pypi/exchangelib/ 参考方案 您需要首先掌握Folder的myfo…

R'relaimpo'软件包的Python端口 - python

我需要计算Lindeman-Merenda-Gold(LMG)分数,以进行回归分析。我发现R语言的relaimpo包下有该文件。不幸的是,我对R没有任何经验。我检查了互联网,但找不到。这个程序包有python端口吗?如果不存在,是否可以通过python使用该包? python参考方案 最近,我遇到了pingouin库。

如何用'-'解析字符串到节点js本地脚本? - python

我正在使用本地节点js脚本来处理字符串。我陷入了将'-'字符串解析为本地节点js脚本的问题。render.js:#! /usr/bin/env -S node -r esm let argv = require('yargs') .usage('$0 [string]') .argv; console.log(argv…

TypeError:'str'对象不支持项目分配,带有json文件的python - python

以下是我的代码import json with open('johns.json', 'r') as q: l = q.read() data = json.loads(l) data['john'] = '{}' data['john']['use…