Меня не устроили rest фреймворки, которые имеются в питоне, и я пытаюсь написать свой на flask (хотя видимо правильнее было бы использовать falcon, как мне теперь кажется).
На данный момент я написал примерно следующую имплементацию.
Возник вопрос, что делать в случае если если есть коллекции элементы которой могут тоже содержать коллекции, а пользователь не хочет получать развернутые коллекции.
В фейсбуке используют 'fields' в queryset, чтобы пользователь мог сам выбрать какие поля ему нужны. Но что если пользователь хочет исключить поля в коллекции уровнем ниже (на один, на два, на сколько угодно уровней)?
Допустим в таком примере для наглядности.
/market/labels/id/products/id/reviews
Пока самым универсальным вариантом мне видится возвращение курсора для всех коллекций без исключения.
Я попытался написать нечто рабочее, но оно работает только если запросить products/id/?fields&every, field,except,reviews, а если запросить produсts, то пользователь все равно увидит reviews у каждого product. Конечно я могу написать проверку типа "если нужно удалить поля, то если ответ является словарем, удали поля из словаря, а если списком словарей, то удали поля в каждом словаре в списке", но это выглядит убого и будет работать только на один уровень глубины. Можно пройтись по всему дереву словарей и удалить поля, но тогда пользователю надо задавать полные пути полей, которые ему нужны.
<code>class ResourceInterface(object):
__metaclass__ = ABCMeta
@abstractmethod
def get_path(self):
pass
@abstractmethod
def set_path(self, path):
pass
@abstractmethod
def on_get(self, *args, **kwargs):
pass
@abstractmethod
def on_post(self, *args, **kwargs):
pass
@abstractmethod
def on_delete(self):
pass
@abstractmethod
def on_put(self):
pass
@abstractmethod
def do_put(self):
pass
@abstractmethod
def do_post(self, *args, **kwargs):
pass
@abstractmethod
# returns serializable result e.g. dict or list or primitive
def do_get(self, *args, **kwargs):
pass
@abstractmethod
def do_delete(self, *args, **kwargs):
pass
class BaseResource(ResourceInterface):
__metaclass__ = ABCMeta
def __init__(self, repository, entity_serializer, path=None):
self.entity_serializer = entity_serializer
self.repository = repository
self.path = None
self.set_path(path)
self.pickler = Pickler(unpicklable=False)
def on_put(self):
pickled_output = self.pickler.flatten(self.do_put())
return jsonpickle.json.encode(pickled_output)
def on_post(self, *args, **kwargs):
pickled_output = self.pickler.flatten(self.do_post(*args, **kwargs))
return jsonpickle.json.encode(pickled_output)
def on_get(self, *args, **kwargs):
try:
pickled_output = self.pickler.flatten(self.do_get(*args, **kwargs))
fields = request.args.get('fields')
if fields:
for field in pickled_output.keys():
if field not in fields:
del pickled_output[field]
return jsonpickle.json.encode(pickled_output)
except ResourceNotFoundError as e:
return str(e), 404
def on_delete(self, *args, **kwargs):
pickled_output = self.pickler.flatten(self.do_delete(*args, **kwargs))
return jsonpickle.json.encode(pickled_output)
def get_path(self):
return self.path
def set_path(self, path):
self.path = path
class ResourceDecorator(ResourceInterface):
def do_delete(self, *args, **kwargs):
return self.implementation.do_delete()
def do_get(self, *args, **kwargs):
return self.implementation.do_get()
def do_post(self, *args, **kwargs):
return self.implementation.do_post()
def do_put(self):
return self.implementation.do_put()
def set_path(self, path):
self.implementation.set_path(path)
def get_path(self):
return self.implementation.get_path()
def on_post(self, *args, **kwargs):
return self.implementation.on_post(args, kwargs)
def on_get(self, *args, **kwargs):
return self.implementation.on_get(args, kwargs)
def on_delete(self):
return self.implementation.on_delete()
def on_put(self):
return self.implementation.on_put()
def __init__(self, implementation):
"""
:type implementation: BaseResource
"""
self.implementation = implementation
class FieldedResourceDecorator(ResourceDecorator):
def do_get(self, *args, **kwargs):
result = super(FieldedResourceDecorator, self).do_get(*args, **kwargs)</code>