Actualmente, la forma en que funciona nuestra serialización, así como la mayoría de los web frameworks, hay algún tipo de invocación de método que vuelca el modelo en algún tipo de formato. En nuestro caso, tenemos un método to_dict()
en cada modelo que construye y devuelve un diccionario de clave-valor con la clave siendo el nombre del campo y el valor siendo la variable de instancia.¿Serializar modelos SQLAlchemy para una API REST respetando el control de acceso?
Todo a lo largo de nuestro código, tenemos fragmentos como el siguiente: json.dumps(**some_model_object.to_dict())
que serializará un some_model_object
a json. Recientemente, hemos decidido exponer algunos recursos internos a nuestros usuarios, pero algunos de estos recursos tienen valores de instancia privados específicos que no queremos transmitir durante la serialización si el usuario solicitante no es un súper usuario.
Estoy tratando de llegar a un diseño limpio que permita una serialización más fácil, así como nos permita serializar a un formato distinto de json. Creo que este es un caso de uso bastante bueno para Diseño/Programación Orientada a Aspectos, donde los aspectos respetan los controles de acceso solicitados y serializan el objeto en función de las necesidades del usuario solicitante.
Aquí es algo similar a lo que tengo ahora:
from framework import current_request
class User(SQLAlchemyDeclarativeModel):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(255))
last_name = Column(Unicode(255))
private_token = Column(Unicode(4096))
def to_dict(self):
serialized = dict((column_name, getattr(self, column_name))
for column_name in self.__table__.c.keys())
# current request might not be bound yet, could be in a unit test etc.
if current_request and not current_request.user.is_superuser():
# we explicitly define the allowed items because if we accidentally add
# a private variable to the User table, then it might be exposed.
allowed = ['id', 'first_name', 'last_name']
serialized = dict((k, v) for k, v in serialized.iteritems() if k in allowed)
return serialized
Como se puede ver, esto es menos que ideal porque ahora tengo que acoplar el modelo de base de datos con la solicitud actual. Si bien esto es muy explícito, el acoplamiento de solicitud es un olor a código y estoy tratando de ver cómo hacerlo limpiamente.
Una forma que he pensado en hacerlo es registrar algunos campos en el modelo de esta manera:
class User(SQLAlchemyDeclarativeModel):
__tablename__ = 'users'
__public__ = ['id', 'first_name', 'last_name']
__internal__ = User.__exposed__ + ['private_token']
id = Column(Integer, primary_key=True)
first_name = Column(Unicode(255))
last_name = Column(Unicode(255))
private_token = Column(Unicode(4096))
Entonces, tendría una clase serializador que se une a la solicitud actual en cada llamada WSGI eso tomará el serializador deseado. Por ejemplo:
import simplejson
from framework import JSONSerializer # json serialization strategy
from framework import serializer
# assume response format was requested as json
serializer.register_serializer(JSONSerializer(simplejson.dumps))
serializer.bind(current_request)
A continuación, en mi opinión, en alguna parte, yo sólo lo haría:
from framework import Response
user = session.query(User).first()
return Response(code=200, serializer.serialize(user))
serialize
se pondrían en práctica de la siguiente manera:
def serialize(self, db_model_obj):
attributes = '__public__'
if self.current_request.user.is_superuser():
attributes = '__private__'
payload = dict((c, getattr(db_model_obj, c))
for c in getattr(db_model_obj, attributes))
return self.serialization_strategy.execute(payload)
Reflexiones sobre la legibilidad y claridad de este enfoque? ¿Es este un enfoque pitónico para el problema?
Gracias de antemano.
No estoy seguro de que nombrar una variable c sea una muy buena idea. :) Y tengo mis reservas sobre algo así como serialization_strategy. Como la clase ya es serializador, es redundante y parece que se está cayendo el nombre del patrón de diseño. ¿Asumo que esto es algo más específico como la estrategia para visitar clases relacionadas? También espero que en realidad no vayas a nombrar tu "marco" de marco. :) –