Spyne - 对复杂模型数组使用嵌套 类

Spyne - Using nested classes for an Array of ComplexModel

关于 Spyne Models and Native Python Types,假设我有两个模型,CompanyEmployee

# server.py
from spyne import (
    Iterable, ComplexModel, Unicode, Integer,
)


class Employee(ComplexModel):
    name = Unicode
    salary = Integer

    def __init__(self, name, salary):
        self.name = name
        self.salary = salary


class Company(ComplexModel):
    name = Unicode
    employees = Iterable(Employee)


    def __init__(self, name, employees):
        self.name = name
        self.employees = employees

现在我可以创建一个 Web 服务 returns 这个数据:

# server.py
from spyne import (
    Application, ServiceBase, rpc
)
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from wsgiref.simple_server import make_server
import logging


class Service(ServiceBase):
    @rpc(_returns=Company)
    def get_company(ctx):
        company = {
            "name": "My Company",
            "employees": [
                Employee("Me", 0),
                Employee("My friend", 10000),
            ]
        }

        return Company(**company)


application = Application(
    [Service],
    "targetnamespace",
    in_protocol=Soap11(validator="lxml"),
    out_protocol=Soap11(),
)

wsgi_application = WsgiApplication(application)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    logging.getLogger("spyne.protocol.xml").setLevel(logging.DEBUG)

    logging.info("Listening to http://127.0.0.1:8000")
    logging.info("WSDL is at: http://localhost:8000/?wsdl")
    server = make_server("127.0.0.1", 8000, wsgi_application)
    server.serve_forever()

我 运行 这个服务器等待请求:

$ python server.py
INFO:root:Listening to http://127.0.0.1:8000
INFO:root:WSDL is at: http://localhost:8000/?wsdl

现在我可以使用zeep发送请求了:

# client.py
from zeep.client import Client


client = Client("http://localhost:8000/?wsdl")
service = client.service
response = service.get_company()
print(response)

结果如预期:

$ python client.py
{
    'name': 'My Company',
    'employees': {
        'Employee': [
            {
                'name': 'Me',
                'salary': 0
            },
            {
                'name': 'My friend',
                'salary': 10000
            }
        ]
    }
}

现在,由于 Employee 属于 Company,将它放在公司的 class 中是有意义的,而且它更干净,更易于维护:

class Company(ComplexModel):
    class Employee(ComplexModel):
        name = Unicode
        salary = Integer

        def __init__(self, name, salary):
            self.name = name
            self.salary = salary


    name = Unicode
    employees = Iterable(Employee)


    def __init__(self, name, employees):
        self.name = name
        self.employees = employees

但现在当我创建服务时:

class Service(ServiceBase):
    @rpc(_returns=Company)
    def get_company(ctx):
        company = {
            "name": "My Company",
            "employees": [
                Company.Employee("Me", 0),
                Company.Employee("My friend", 10000),
            ]
        }

        return Company(**company)

客户端(python client.py)报错:

TypeError: 'NoneType' object is not iterable

而服务器(python server.py)给出了这个错误:

TypeError: Argument must be bytes or unicode, got 'ModelBaseMeta'

我该如何解决这个问题?

你不知道。这不应该起作用。

以下:

class Company(ComplexModel):
    class Employee(ComplexModel):
        # ...
    # ...

与以下伪代码相同:

class EmployeeTopLevel(ComplexModel):
    # ...

class Company(ComplexModel):
    Employee = EmployeeTopLevel
    # ...

你现在可能明白为什么这是个坏主意了。 ModelBase 元类不支持 ModelBase 子级的嵌套。您正在尝试将 类 用于包和模块的设计目的。您应该在模块级别定义 类。

这里有一个解决方法:

class Company(ComplexModel):
    class Employee(ComplexModel):
        name = Unicode
        salary = Integer

        def __init__(self, name, salary):
            self.name = name
            self.salary = salary

    _type_info = [
        ('name', Unicode),
        ('employees', Iterable(Employee)),
    ]

    def __init__(self, name, employees):
        self.name = name
        self.employees = employees

之所以有效,是因为它跳过了模型元类的字段检测逻辑,直接使用 user-supplied _type_info 列表。