Source code for ripozo.viewsets.relationships.relationship

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

from ripozo.exceptions import RestException
from ripozo.viewsets.constructor import ResourceMetaClass

import logging
import six

logger = logging.getLogger(__name__)


[docs]class Relationship(object): """ Defines a relationship on a resource. This allows you to create related resources and construct them appropriately. As usual, the actual response is created by an adapter which must determine whether to return the whole resource, a link, etc... """ _resource_meta_class = ResourceMetaClass def __init__(self, name, property_map=None, relation=None, embedded=False, required=False): """ :param unicode name: :param dict property_map: A map of the parent's property name to the corresponding related fields properties. For example, it may be called "child" on the parent but it corresponds to the id field on the related field. :param unicode relation: The name of the resource class that this relation is a type of. It uses a string so that you do not have to worry about the order of how relations were defined. It looks up the actual type from the ResourceMetaClass. :param bool embedded: Indicates whether the related resource should be embedded in the parent resource when returned. Otherwise, a more basic representation will be used (e.g. a link or id) """ self.property_map = property_map or {} self._relation = relation self.embedded = embedded self.required = required self.name = name @property def relation(self): """ The ResourceBase subclass that describes the related object If no _relation property is available on the instance it returns None. Raises a key error when the relation keyword argument passed on construction is not available in the self._resource_meta_class.registered_names_map dictionary (By default the self._resource_meta_class is the ResourceMetaClass). :return: The ResourceBase subclass that describes the related resource :rtype: type :raises: KeyError """ return self._resource_meta_class.registered_names_map[self._relation]
[docs] def construct_resource(self, properties, query_args=None): """ Takes the properties from the parent and and maps them to the named properties for the parent resource to its relationships :param dict properties: :return: An instance of a self.relation class that corresponds to this related resource :rtype: rest.viewsets.resource_base.ResourceBase """ logger.debug('Constructing resource {0} of type {1}'.format(self.name, self.relation)) related_properties = self._map_pks(properties) resource = None if related_properties: resource = self.relation(properties=related_properties, query_args=query_args, include_relationships=self.embedded) if self.required and (not resource or not resource.has_all_pks): raise RestException('The relationship {0} could not construct a valid {1}' ' with all of its pks. Properties' ' {2}'.format(self.name, self.relation, related_properties)) elif not resource or not resource.has_all_pks: return None return resource
[docs] def remove_child_resource_properties(self, properties): """ Removes the properties that are supposed to be on the child resource and not on the parent resource. It copies the properties argument before it removes the copied values. It does not have side effects in other words. :param dict properties: The properties that are in the related resource map that should not be in the parent resource. :return: a dictionary of the updated properties :rtype: :py:class:`dict` """ properties = properties.copy() for key in six.iterkeys(self.property_map): properties.pop(key, None) return properties
def _map_pks(self, parent_properties): """ Takes a dictionary of the values of the parent resources properties. It then maps those properties to the named properties of the related resource and creates a dictionary of the related resources property values. Raises a KeyError if the parent does not contain keys that matches every key in the self.property_map :param dict parent_properties: A dictionary of the parent resource's properties. The key is the name of the property and the value is the parent resources value for that property :return: A dictionary of the related resources properties. The key is the name of the related resource's property and the value is the value of that resource's property. :rtype: :py:class:`dict` :raises: KeyError """ properties = {} for parent_prop, prop in six.iteritems(self.property_map): val = parent_properties.pop(parent_prop, None) if val is not None: properties[prop] = val properties.update(parent_properties.pop(self.name, {})) return properties