Example #1
0
    def create_subclass(self, params, **kwargs):
        """
            Subclasses the LayerSelectionResource instance's class for the given config_entity and layer.
        :param params Must contain a 'config_entity__id' and 'layer__id'
        :return:
        """

        layer = self.resolve_layer(params)
        config_entity = self.resolve_config_entity(params)

        logger.debug(
            "Resolving LayerSelection for config_entity %s, layer %s" %
            (config_entity.key, layer.db_entity.key))
        layer_selection_class = get_or_create_layer_selection_class_for_layer(
            layer, config_entity)
        # Have the LayerPublisher create the LayerSelection instance for the user if needed
        update_or_create_layer_selections_for_layer(
            layer, users=[self.resolve_user(params)])

        if not layer_selection_class:
            raise Exception(
                "Layer with db_entity_key %s has no feature_class. Its LayerSelections should not be requested"
                % layer.db_entity_key)
        return get_dynamic_resource_class(self.__class__,
                                          layer_selection_class)
    def create_subclass(self, params, **kwargs):
        land_use_definition_fixture_class = resolve_fixture_class(
            "built_form",
            "land_use_definition",
            ClientLandUseDefinition,
            settings.CLIENT)

        return get_dynamic_resource_class(
            self.__class__,
            land_use_definition_fixture_class)
Example #3
0
    def create_subclass(self, params, **kwargs):
        """
            Subclasses the LayerSelectionResource instance's class for the given config_entity and layer.
        :param params Must contain a 'config_entity__id' and 'layer__id'
        :return:
        """

        layer = self.resolve_layer(params)
        config_entity = self.resolve_config_entity(params)

        logger.debug("Resolving LayerSelection for config_entity %s, layer %s" % (config_entity.key, layer.db_entity.key))
        layer_selection_class = get_or_create_layer_selection_class_for_layer(layer, config_entity)
        # Have the LayerPublisher create the LayerSelection instance for the user if needed
        update_or_create_layer_selections_for_layer(layer, users=[self.resolve_user(params)])

        if not layer_selection_class:
            raise Exception("Layer with db_entity_key %s has no feature_class. Its LayerSelections should not be requested" % layer.db_entity_key)
        return get_dynamic_resource_class(
            self.__class__,
            layer_selection_class
        )
    def resolve_resource_class(cls,
                               model_class,
                               related_descriptors=None,
                               queryset=None,
                               base_resource_class=None,
                               object_class=None,
                               no_cache=False,
                               use_version_fields=False,
                               only_abstract_resources=True,
                               additional_fields_dict=None,
                               is_join_query=False,
                               limit_fields=None,
                               for_template=False):
        """
            Match the model_class to an existing resource class by iterating through subclasses of self.__class__
            If no match occurs generate one if db_entity is specified, else None
        :param model_class:
        :param related_descriptors: Optionally provided a dictionary of related descriptors of the model_class to use to create related resource fields
        in the case of dynamic resource creation. If unspecified, related_descriptors are found using the base resource class's resolve_related_descriptors method
        :param no_cache: Don't use cached resources. Specifying queryset also prevents the cache being consulted
        :param use_version_fields: Special case for versioned models. Instructs the resource class creation
         to create dynamic fields to look in the model_class's Revisionable._version_field_dict to resolve
         the value of a field before looking at the database (current object) version of the field.
        :param additional_fields_dict. Optional dict that gives the resource class additional fields
        with values. This is not for related fields, rather special cases like passing the layer_selection
        to the FeatureResource.
        :param for_template: Currently just used for Features, but this indicates a template version of the
        resource is being sought
        :param is_join_query: True if the is a join query
        table or similar. This means that the queryset isn't actually valid yet
        :return: The matching or created resource
        """
        # Never seek a cached resource if a queryset or no_cache is specified
        allow_caching = queryset is None and not no_cache

        cls.logger.info("Resolving Resource Class for model_class %s with query %s and allow_caching %s and only_abstract_resources %s and base_resource_class %s",
            model_class.__name__, queryset, allow_caching, only_abstract_resources, base_resource_class)
        if not base_resource_class and allow_caching:
            # Try to get a cached resource if a base_resource_class is present and we allow caching
            # of this resource. We never allow caching if the queryset changes from request to request
            # Instead we subclass the best matching existing resource class
            resources = FootprintResource.match_existing_resources(model_class)
            if len(resources) > 1:
                logging.warn("Too many resources for model class: %s. Resources; %s" % (model_class, resources))
            if (len(resources) > 0):
                cls.logger.info("Found existing resource class %s for model class %s" % (resources[0], model_class))
                # Done, return the existing resource
                return resources[0]

        # If we don't allow caching or no resource was found
        if only_abstract_resources:
            # See if we can match an abstract resource to be our base.
            # We look for the best matching resource who's model class matches or is a superclass or our model class.
            base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True)
            cls.logger.info("Matching abstract base resource classes: %s" % base_resource_classes)
            base_resource_class = base_resource_classes[0] if len(base_resource_classes)==1 else base_resource_class
        else:
            # Fist try to find a concrete one that we already created
            concrete_base_resource_classes = FootprintResource.match_existing_resources(model_class, allow_abstract_resources=False)
            if len(concrete_base_resource_classes)==1:
                # Found a concrete resource class that was already created for this model
                # If this isn't a join query we can return this resource class
                base_resource_class = concrete_base_resource_classes[0]
                if not is_join_query:
                    return base_resource_class
            else:
                # Try to find an abstract one
                all_base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True)
                cls.logger.info("Matching abstract base resource classes: %s" % all_base_resource_classes)
                base_resource_class = all_base_resource_classes[0] if len(all_base_resource_classes)==1 else base_resource_class

        if is_join_query:
            # Join queries must pass in the limited fields from the layer_selection.result_map.result_fields
            limit_api_fields = limit_fields
        else:
            # Non-join queries find their fields from the model_class
            limit_api_fields = limited_api_fields(model_class, for_template=for_template)

        resource_class = base_resource_class or cls
        if base_resource_class:
            cls.logger.info("Resolved base_resource_class: %s" % base_resource_class)
        else:
            cls.logger.info("Could not resolve a base_resource_class. Using resource class %s" % cls)
        related_descriptors = related_descriptors or \
                              resource_class.resolve_related_descriptors(model_class) or \
                              {}

        cls.logger.info("Creating a resource subclass of %s for model_class %s with the following related descriptors %s" % (
            resource_class,
            model_class,
            ', '.join(map_dict(
                lambda related_field_name, related_descriptor: related_field_name,
                related_descriptors
            ))))
        related_fields = merge(additional_fields_dict or {}, map_dict_to_dict(
            lambda related_field_name, relationship_dict: cls.related_resource_field(
                related_field_name,
                merge(
                    dict(
                        # This is passed in, but might be overriden in the relationship_dict
                        use_version_fields=use_version_fields
                    ),
                    # Already a relationship dict
                    relationship_dict if isinstance(relationship_dict, dict) else dict(),
                    # Not a relationship dict, just a descriptor, wrap it
                    dict(descriptor=relationship_dict) if not isinstance(relationship_dict, dict) else dict(),
                ),
                # Always allow cached related resource classes.
                # I think this is always safe
                no_cache=False),
            related_descriptors))
        meta = merge(
            dict(fields=limit_api_fields) if limit_api_fields else
                dict(excludes=(resource_class.Meta.excludes if hasattr(
                  resource_class.Meta, 'excludes') else []) + cls.dynamic_excludes(
                model_class)),
            dict(queryset=queryset, object_class=object_class) if queryset else dict())
        return get_dynamic_resource_class(
            resource_class,
            model_class,
            # Created related fields from the related_descriptors
            fields=related_fields,
            # Optionally set the fields and queryset on the Meta class
            meta_fields=meta
        )
Example #5
0
    def resolve_resource_class(cls,
                               model_class,
                               related_descriptors=None,
                               queryset=None,
                               base_resource_class=None,
                               object_class=None,
                               no_cache=False,
                               use_version_fields=False,
                               only_abstract_resources=True,
                               additional_fields_dict=None,
                               is_join_query=False,
                               limit_fields=None,
                               for_template=False):
        """
            Match the model_class to an existing resource class by iterating through subclasses of self.__class__
            If no match occurs generate one if db_entity is specified, else None
        :param model_class:
        :param related_descriptors: Optionally provided a dictionary of related descriptors of the model_class to use to create related resource fields
        in the case of dynamic resource creation. If unspecified, related_descriptors are found using the base resource class's resolve_related_descriptors method
        :param no_cache: Don't use cached resources. Specifying queryset also prevents the cache being consulted
        :param use_version_fields: Special case for versioned models. Instructs the resource class creation
         to create dynamic fields to look in the model_class's Revisionable._version_field_dict to resolve
         the value of a field before looking at the database (current object) version of the field.
        :param additional_fields_dict. Optional dict that gives the resource class additional fields
        with values. This is not for related fields, rather special cases like passing the layer_selection
        to the FeatureResource.
        :param for_template: Currently just used for Features, but this indicates a template version of the
        resource is being sought
        :param is_join_query: True if the is a join query
        table or similar. This means that the queryset isn't actually valid yet
        :return: The matching or created resource
        """
        # Never seek a cached resource if a queryset or no_cache is specified
        allow_caching = queryset is None and not no_cache

        cls.logger.info("Resolving Resource Class for model_class %s with query %s and allow_caching %s and only_abstract_resources %s and base_resource_class %s",
            model_class.__name__, queryset, allow_caching, only_abstract_resources, base_resource_class)
        if not base_resource_class and allow_caching:
            # Try to get a cached resource if a base_resource_class is present and we allow caching
            # of this resource. We never allow caching if the queryset changes from request to request
            # Instead we subclass the best matching existing resource class
            resources = FootprintResource.match_existing_resources(model_class)
            if len(resources) > 1:
                logging.warn("Too many resources for model class: %s. Resources; %s" % (model_class, resources))
            if (len(resources) > 0):
                cls.logger.info("Found existing resource class %s for model class %s" % (resources[0], model_class))
                # Done, return the existing resource
                return resources[0]

        # If we don't allow caching or no resource was found
        if only_abstract_resources:
            # See if we can match an abstract resource to be our base.
            # We look for the best matching resource who's model class matches or is a superclass or our model class.
            base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True)
            cls.logger.info("Matching abstract base resource classes: %s" % base_resource_classes)
            base_resource_class = base_resource_classes[0] if len(base_resource_classes)==1 else base_resource_class
        else:
            # Fist try to find a concrete one that we already created
            concrete_base_resource_classes = FootprintResource.match_existing_resources(model_class, allow_abstract_resources=False)
            if len(concrete_base_resource_classes)==1:
                # Found a concrete resource class that was already created for this model
                # If this isn't a join query we can return this resource class
                base_resource_class = concrete_base_resource_classes[0]
                if not is_join_query:
                    return base_resource_class
            else:
                # Try to find an abstract one
                all_base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True)
                cls.logger.info("Matching abstract base resource classes: %s" % all_base_resource_classes)
                base_resource_class = all_base_resource_classes[0] if len(all_base_resource_classes)==1 else base_resource_class

        if is_join_query:
            # Join queries must pass in the limited fields from the layer_selection.result_map.result_fields
            limit_api_fields = limit_fields
        else:
            # Non-join queries find their fields from the model_class
            limit_api_fields = limited_api_fields(model_class, for_template=for_template)

        resource_class = base_resource_class or cls
        if base_resource_class:
            cls.logger.info("Resolved base_resource_class: %s" % base_resource_class)
        else:
            cls.logger.info("Could not resolve a base_resource_class. Using resource class %s" % cls)
        related_descriptors = related_descriptors or \
                              resource_class.resolve_related_descriptors(model_class) or \
                              {}

        cls.logger.info("Creating a resource subclass of %s for model_class %s with the following related descriptors %s" % (
            resource_class,
            model_class,
            ', '.join(map_dict(
                lambda related_field_name, related_descriptor: related_field_name,
                related_descriptors
            ))))
        related_fields = merge(additional_fields_dict or {}, map_dict_to_dict(
            lambda related_field_name, relationship_dict: cls.related_resource_field(
                related_field_name,
                merge(
                    dict(
                        # This is passed in, but might be overriden in the relationship_dict
                        use_version_fields=use_version_fields
                    ),
                    # Already a relationship dict
                    relationship_dict if isinstance(relationship_dict, dict) else dict(),
                    # Not a relationship dict, just a descriptor, wrap it
                    dict(descriptor=relationship_dict) if not isinstance(relationship_dict, dict) else dict(),
                ),
                # Always allow cached related resource classes.
                # I think this is always safe
                no_cache=False),
            related_descriptors))
        meta = merge(
            dict(fields=limit_api_fields) if limit_api_fields else
                dict(excludes=(resource_class.Meta.excludes if hasattr(
                  resource_class.Meta, 'excludes') else []) + cls.dynamic_excludes(
                model_class)),
            dict(queryset=queryset, object_class=object_class) if queryset else dict())
        return get_dynamic_resource_class(
            resource_class,
            model_class,
            # Created related fields from the related_descriptors
            fields=related_fields,
            # Optionally set the fields and queryset on the Meta class
            meta_fields=meta
        )