B
B
Boris_B2015-05-08 18:43:50
Python
Boris_B, 2015-05-08 18:43:50

What api and implementation should be for REST collections in python?

I was not satisfied with the rest frameworks that are available in python, and I'm trying to write my own in flask (although it would probably be more correct to use falcon, as it seems to me now).
At the moment I have written approximately the following implementation.
The question arose of what to do if there are collections whose elements can also contain collections, and the user does not want to receive expanded collections.
Facebook uses 'fields' in the queryset so that the user can choose which fields he needs. But what if the user wants to exclude fields in the collection one level down (one, two, any number of levels)?
Let's take this example for clarity.
/market/labels/id/products/id/reviews
So far, I see the return of the cursor for all collections without exception as the most universal option.
I tried to write something working, but it only works if you request products/id/?fields&every, field,except,reviews, and if you request products, the user will still see reviews for each product. Of course, I can write a check like "if you need to remove fields, then if the answer is a dictionary, remove the fields from the dictionary, and if the list of dictionaries, then remove the fields in each dictionary in the list", but this looks shabby and will only work one level deep . You can go through the entire tree of dictionaries and delete the fields, but then the user must specify the full paths of the fields that he needs.

<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>

Answer the question

In order to leave comments, you need to log in

1 answer(s)
B
bromzh, 2015-05-09
@bromzh

But what if the user wants to exclude fields in the collection one level down
? Use a dot, then consider the included/excluded fields recursively.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question