Сериализация моделей SQLAlchemy для REST API с соблюдением контроля доступа ?

В настоящее время, как наша, так и большинство веб-фреймворков, сериализация работает так, что существует некий тип вызова метода, который выгружает модель в какой-то формат. В нашем случае у нас есть метод 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))

сериализацию можно было бы реализовать следующим образом:

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)

Мысли о читаемости и ясности этого подхода? Это питонический подход к проблеме?

Заранее спасибо.

7
задан Mahmoud Abdelkader 8 March 2011 в 19:37
поделиться