Spyne - 对复杂模型数组使用嵌套 类
Spyne - Using nested classes for an Array of ComplexModel
关于 Spyne Models and Native Python Types,假设我有两个模型,Company
和 Employee
:
# 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
列表。
关于 Spyne Models and Native Python Types,假设我有两个模型,Company
和 Employee
:
# 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
列表。