В настоящее время, как и наша, как и большинство веб-фреймворков, сериализация работает, есть некоторый тип вызова метода, который выгружает модель в некоторый тип формата. В нашем случае у нас есть метод to_dict()
для каждой модели, который создает и возвращает словарь ключ-значение, где ключом является имя поля, а значением является переменная экземпляра.
Во всем нашем коде есть фрагменты, подобные следующему: json.dumps(**some_model_object.to_dict())
, который сериализует some_model_object
в json. Недавно мы решили предоставить некоторые внутренние ресурсы нашим пользователям, но некоторые из этих ресурсов имеют конкретные значения частных экземпляров, которые мы не хотим передавать обратно во время сериализации, если запрашивающий пользователь не является суперпользователем.
Я пытаюсь придумать чистый дизайн, который позволит упростить сериализацию, а также позволит сериализовать в формат, отличный от json. Я думаю, что это довольно хороший вариант использования для Аспектно-ориентированного проектирования / программирования, где аспекты учитывают запрашивающие элементы управления доступом и сериализуют объект на основе разрешений запрашивающего пользователя.
Вот что-то похожее на то, что у меня сейчас:
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
Как видите, это далеко не идеально, потому что теперь мне нужно связать модель базы данных с текущим запросом. Хотя это очень явно, связь запросов - это запах кода, и я пытаюсь понять, как это сделать чисто.
Один из способов, который я задумал сделать, - это зарегистрировать некоторые поля в модели следующим образом:
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))
Тогда у меня будет класс сериализатора, связанный с текущим запросом при каждом вызове WSGI, который будет принимать нужный сериализатор. Например:
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)
Тогда, на мой взгляд, я бы просто сделал:
from framework import Response
user = session.query(User).first()
return Response(code=200, serializer.serialize(user))
serialize
будет реализовано следующим образом:
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)
Мысли о читаемости и ясности этого подхода? Это питонный подход к проблеме?
Заранее спасибо.