How do you @rpc _returns polymorphic types in spyn

2019-08-24 17:05发布

问题:

Edit

Example,

class A(ComplexModel):
    Id = Unicode

class B(ComplexModel):
    __extends__ = A
    Name = Unicode

@rpc(String, _returns=A)
def hello(self, name):
    ret = B()
    B.Id = '123'
    B.Name = name
    return ret

How do you handle this behavior so it doesn't return an object of A?


How would I write the spyne decorators to correctly return more than one type? If, for example, _returns is set to ZObj then returning an XAccount (like in the code) doesn't do anything.

Can I write the XAccount object so that it extends ZObj and is a valid return type?

@rpc(String, _returns=(ZObj, XAccount))
def hello(self, name):
    acc = XAccount(
        user_name = name.upper(),
        first_name = name,
        last_name = 'last ' + name
    )
    return acc

Class examples....

class ZObj(ComplexModel):
    id = Unicode(pattern='[a-zA-Z0-9]{32}|\d+')

class Account(DeclarativeBase):
    __tablename__ = 'account'

    id = Column(Integer, primary_key=True)
    user_name = Column(String(256))
    first_name = Column(String(256))
    last_name = Column(String(256))


class XAccount(TableModel):
    __table__ = Account.__table__

回答1:

Deleting my previous answer as you apparently need polymorphism, not multiple return types.

So, There are two ways of doing polymorphism in Spyne: The Python way and the Spyne way.

Let:

class A(ComplexModel):
    i = Integer

class B(A):
    s = Unicode

class C(A):
    d = DateTime

The Python way uses duck typing to return values.

Let's define a generic class:

class GenericA(ComplexModel):
    i = Integer
    s = Unicode
    d = DateTime

and use it as return value of our sample service:

class SomeService(ServiceBase):
    @rpc(Unicode(values=['A', 'B', 'C']), _returns=GenericA)
    def get_some_a(self, type_name):
        # (...)

This way, you get your data, but it's tagged as a GenericA object. If you don't care about this, you can create a class that has all types from all objects (assuming attributes with same names have the same type) and just be done with it. This is easy, stable and works today.

If that's not enough for your needs, you have to do the polymorphism the Spyne way. To do that, first set your return type to the base class:

class SomeService(ServiceBase):
    @rpc(Unicode(values=['A', 'B', 'C']), _returns=A)
    def get_some_a(self, type_name):
        # (...)

and tag your output protocol to be polymorphic:

application = Application([SomeService], 'tns',
    in_protocol=Soap11(validator='lxml'),
    out_protocol=Soap11(polymorphic=True)
)

This requires at least Spyne-2.12.

Working example: https://github.com/arskom/spyne/blob/a1b3593f3754a9c8a6787c29ff50f591db89fd49/examples/xml/polymorphism.py



回答2:

Also, you don't need to do this:

class Account(DeclarativeBase):
    __tablename__ = 'account'

    id = Column(Integer, primary_key=True)
    user_name = Column(String(256))
    first_name = Column(String(256))
    last_name = Column(String(256))

class XAccount(TableModel):
    __table__ = Account.__table__

This works just as well:

from spyne import TTableModel

TableModel = TTableModel() # Think of this as Spyne's declarative_base

class Account(TableModel):
    __tablename__ = 'account'

    id = Integer(primary_key=True)
    user_name = Unicode(256)
    first_name = Unicode(256)
    last_name = Unicode(256)

where your table metadata is in TableModel.Attributes.sqla_metadata

Objects created this way are usable in both in SQLAlchemy queries and as Spyne types.