Source code for durga.collection

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import itertools
import json

import requests

from . import exceptions
from .element import Element


[docs]class Collection(object): response = None def __init__(self, url, resource): self.url = url self.resource = resource self._reset_request()
[docs] def all(self): self._reset_request() return self
[docs] def filter(self, **kwargs): self._reset_data() self.request.params.update(kwargs) return self
[docs] def values(self, *fields): """Return a list of dictionaries instead of Element instances. The optional positional arguments, \*fields, can be used to limit the fields that are returned. """ self._reset_data() self.fields = fields self.as_dict = True self.as_list = False return self
[docs] def values_list(self, *fields, **kwargs): """Return a list of tuples instead of Element instances. The optional positional arguments, \*fields, can be used to limit the fields that are returned. If only a single field is passed in, the flat parameter can be passed in too. If True, this will mean the returned results are single values, rather than one-tuples. """ self._reset_data() self.flat = kwargs.pop('flat', False) if kwargs: raise TypeError("Unexpected keyword arguments to values_list: {}".format(list(kwargs))) if self.flat and len(fields) != 1: raise TypeError(("If 'flat' is used values_list must be called " "with exactly one field.")) self.fields = fields self.as_dict = False self.as_list = True return self
[docs] def order_by(self): raise NotImplementedError return self
[docs] def count(self): return len(self)
[docs] def get(self, **kwargs): try: self.request.url = self.get_element_url(kwargs.pop(self.resource.id_attribute)) except AttributeError: pass self.filter(**kwargs) count = self.count() if count > 1: raise exceptions.MultipleObjectsReturnedError elif count == 0: raise exceptions.ObjectNotFoundError element = self.elements[0] self._reset_request() return element
[docs] def create(self, data): """Create a new remote resource from a dictionary. At first the data will be validated. After successful validation the data is converted to JSON. The response of the POST request is returned afterwards. """ self.resource.validate([data]) request = requests.Request('POST', self.url, data=json.dumps(data)) return self.resource.dispatch(request)
[docs] def update(self, data): """Update all Elements of this Collection with data from a dictionary. The data dictionary is used to update the data of all Elements of this Collection. The updated Elements are validated and their data is converted to JSON. A PUT request is made for each Element. Finally a list of all responses is returned. """ responses = [] for element in self.elements: element.update(data) element.validate() request = requests.Request('PUT', self.url, data=json.dumps(element.get_data())) responses.append(self.resource.dispatch(request)) return responses
[docs] def delete(self): """Delete all Elements of this Collection. Return the response for each deleted Element as a list. """ return [element.delete() for element in self.elements]
def _reset_data(self): self.data = self.validated_data = self._elements = None def _reset_fields(self): self.as_dict = self.as_list = self.flat = False self.fields = None def _reset_request(self): self._reset_data() self._reset_fields() self.request = requests.Request('GET', self.url) @property def elements(self): if not self._elements: self.response = self.resource.dispatch(self.request) self.data = self.resource.extract(self.response) self.validated_data = self.resource.validate(self.data) if self.as_dict or self.as_list: self._elements = [self.get_values(data) for data in self.validated_data] else: self._elements = [self.get_element(data) for data in self.validated_data] return self._elements
[docs] def get_element(self, data): """Return an Element instance holding the passed data dictionary.""" if not hasattr(self, 'element_class'): prefix = self.resource.path.title().replace('/', '') self.element_class = str('{0}Element'.format(prefix)) if not hasattr(self, 'element_base'): self.element_base = getattr(self.resource, 'element', Element) try: element = type(self.element_class, (self.element_base,), data)(self.resource, data) except TypeError: error = 'Failed to create Element. Data from request: {}'.format(data) raise exceptions.DurgaError(error) return element
[docs] def get_values(self, data): """Return either a dictionary, a tuple or a single field. The data type and the fields returned are defined by using values() or values_list(). """ if self.as_dict: if self.fields: values = {field: data[field] for field in self.fields} else: values = data elif self.flat: values = data[self.fields[0]] else: if self.fields: values = [data[field] for field in self.fields] else: values = data.values() values = tuple(values) return values
[docs] def get_element_url(self, id): if self.resource.schema: self.resource.schema._schema[self.resource.id_attribute].validate(id) return '{0}/{1}'.format(self.url, id)
def __getitem__(self, key): """Return a single Element or a slice of Elements.""" if isinstance(key, slice): return itertools.islice(self.elements, *key.indices(self.count())) else: return self.elements[key] def __iter__(self): return iter(self.elements) def __len__(self): return len(self.elements)