Ejemplo n.º 1
0
    def handle_facet_parameters(self, kwargs):
        if kwargs.get('faceted', False):
            raise SearchFieldError(
                "FacetField (%s) does not accept the 'faceted' argument." %
                self.instance_name)

        if not kwargs.get('null', True):
            raise SearchFieldError(
                "FacetField (%s) does not accept False for the 'null' argument."
                % self.instance_name)

        if not kwargs.get('indexed', True):
            raise SearchFieldError(
                "FacetField (%s) does not accept False for the 'indexed' argument."
                % self.instance_name)

        if kwargs.get('facet_class'):
            raise SearchFieldError(
                "FacetField (%s) does not accept the 'facet_class' argument." %
                self.instance_name)

        self.facet_for = None
        self.facet_class = None

        # Make sure the field is nullable.
        kwargs['null'] = True

        if 'facet_for' in kwargs:
            self.facet_for = kwargs['facet_for']
            del (kwargs['facet_for'])

        return kwargs
Ejemplo n.º 2
0
    def handle_facet_parameters(self, kwargs):
        if kwargs.get("faceted", False):
            raise SearchFieldError(
                "FacetField (%s) does not accept the 'faceted' argument." %
                self.instance_name)

        if not kwargs.get("null", True):
            raise SearchFieldError(
                "FacetField (%s) does not accept False for the 'null' argument."
                % self.instance_name)

        if not kwargs.get("indexed", True):
            raise SearchFieldError(
                "FacetField (%s) does not accept False for the 'indexed' argument."
                % self.instance_name)

        if kwargs.get("facet_class"):
            raise SearchFieldError(
                "FacetField (%s) does not accept the 'facet_class' argument." %
                self.instance_name)

        self.facet_for = None
        self.facet_class = None

        # Make sure the field is nullable.
        kwargs["null"] = True

        if "facet_for" in kwargs:
            self.facet_for = kwargs["facet_for"]
            del kwargs["facet_for"]

        return kwargs
    def collect_fields(self, index):
        for fieldname, field_object in index.fields.items():
            if field_object.document is True:
                if field_object.index_fieldname != self.document_field:
                    raise SearchFieldError("All 'SearchIndex' classes must use the same '%s' fieldname for the 'document=True' field. Offending index is '%s'." % (self.document_field, index))

            # Stow the index_fieldname so we don't have to get it the hard way again.
            if fieldname in self._fieldnames and field_object.index_fieldname != self._fieldnames[fieldname]:
                # We've already seen this field in the list. Raise an exception if index_fieldname differs.
                raise SearchFieldError("All uses of the '%s' field need to use the same 'index_fieldname' attribute." % fieldname)

            self._fieldnames[fieldname] = field_object.index_fieldname

            # Stow the facet_fieldname so we don't have to look that up either.
            if hasattr(field_object, 'facet_for'):
                if field_object.facet_for:
                    self._facet_fieldnames[field_object.facet_for] = fieldname
                else:
                    self._facet_fieldnames[field_object.instance_name] = fieldname

            # Copy the field in so we've got a unified schema.
            if not field_object.index_fieldname in self.fields:
                self.fields[field_object.index_fieldname] = field_object
                self.fields[field_object.index_fieldname] = copy.copy(field_object)
            else:
                # If the field types are different, we can mostly
                # safely ignore this. The exception is ``MultiValueField``,
                # in which case we'll use it instead, copying over the
                # values.
                if field_object.is_multivalued == True:
                    old_field = self.fields[field_object.index_fieldname]
                    self.fields[field_object.index_fieldname] = field_object
                    self.fields[field_object.index_fieldname] = copy.copy(field_object)

                    # Switch it so we don't have to dupe the remaining
                    # checks.
                    field_object = old_field

                # We've already got this field in the list. Ensure that
                # what we hand back is a superset of all options that
                # affect the schema.
                if field_object.indexed is True:
                    self.fields[field_object.index_fieldname].indexed = True

                if field_object.stored is True:
                    self.fields[field_object.index_fieldname].stored = True

                if field_object.faceted is True:
                    self.fields[field_object.index_fieldname].faceted = True

                if field_object.use_template is True:
                    self.fields[field_object.index_fieldname].use_template = True

                if field_object.null is True:
                    self.fields[field_object.index_fieldname].null = True
Ejemplo n.º 4
0
    def prepare(self, obj):
        """
        Takes data from the provided object and prepares it for storage in the
        index.
        """
        # Give priority to a template.
        if self.use_template:
            return self.prepare_template(obj)
        elif self.model_attr is not None:
            # Check for `__` in the field for looking through the relation.
            attrs = self.model_attr.split('__')
            current_object = obj

            for attr in attrs:
                if not hasattr(current_object, attr):
                    if attr not in dir(current_object):
                        raise SearchFieldError(
                            "The model '%s' does not have a model_attr '%s'." %
                            (repr(obj), attr))

                try:
                    current_object = getattr(current_object, attr, None)
                except ObjectDoesNotExist:
                    current_object = None

                if current_object is None:
                    if self.has_default():
                        current_object = self._default
                        # Fall out of the loop, given any further attempts at
                        # accesses will fail misreably.
                        break
                    elif self.null:
                        current_object = None
                        # Fall out of the loop, given any further attempts at
                        # accesses will fail misreably.
                        break
                    else:
                        raise SearchFieldError(
                            "The model '%s' has an empty model_attr '%s' and doesn't allow a default or null value."
                            % (repr(obj), attr))

            if callable(current_object):
                return current_object()

            return current_object

        if self.has_default():
            return self.default
        else:
            return None
Ejemplo n.º 5
0
    def __init__(self, **kwargs):
        if kwargs.get('use_template') is True:
            raise SearchFieldError(
                "'%s' fields can not use templates to prepare their data." %
                self.__class__.__name__)

        super(ESAttachmentField, self).__init__(**kwargs)
 def _field_mapping(self):
     mapping = {}
     
     if self._cached_field_mapping:
         return self._cached_field_mapping
     
     for model, index in self.get_indexes().items():
         for field_name, field_object in index.fields.items():
             if field_name in mapping and field_object.index_fieldname != mapping[field_name]['index_fieldname']:
                 # We've already seen this field in the list. Raise an exception if index_fieldname differs.
                 raise SearchFieldError("All uses of the '%s' field need to use the same 'index_fieldname' attribute." % field_name)
             
             facet_fieldname = None
             
             if hasattr(field_object, 'facet_for'):
                 if field_object.facet_for:
                     facet_fieldname = field_object.facet_for
                 else:
                     facet_fieldname = field_object.instance_name
             
             mapping[field_name] = {
                 'index_fieldname': field_object.index_fieldname,
                 'facet_fieldname': facet_fieldname,
             }
     
     self._cached_field_mapping = mapping
     return mapping
Ejemplo n.º 7
0
    def prepare_template(self, obj):
        """
        Flattens an object for indexing.

        This loads a template
        (``search/indexes/{app_label}/{model_name}_{field_name}.txt``) and
        returns the result of rendering that template. ``object`` will be in
        its context.
        """
        if self.instance_name is None and self.template_name is None:
            raise SearchFieldError(
                "This field requires either its instance_name variable to be populated or an explicit template_name in order to load the correct template."
            )

        if self.template_name is not None:
            template_names = self.template_name

            if not isinstance(template_names, (list, tuple)):
                template_names = [template_names]
        else:
            app_label, model_name = get_model_ct_tuple(obj)
            template_names = [
                'search/indexes/%s/%s_%s.txt' %
                (app_label, model_name, self.instance_name)
            ]

        t = loader.select_template(template_names)
        return t.render({'object': obj})
Ejemplo n.º 8
0
    def prepare_template(self, obj):
        """
        Does the same as :func:`haystack.fields.CharField.prepare_template`,
        except it replaces all occurrences of ``{app_label}``, ``{model_name}``
        and ``{field_name}`` with the respective values on the given path(s).
        """
        if self.instance_name is None and self.template_name is None:
            raise SearchFieldError("This field requires either its "
                                   "instance_name variable to be populated or "
                                   "an explicit template_name in order to "
                                   "load the correct template.")

        if self.template_name is not None:
            template_names = self.template_name
            if not isinstance(template_names, (list, tuple)):
                template_names = [template_names]
        else:
            template_names = [
                'search/indexes/{app_label}/{model_name}_{field_name}.txt'
            ]

        resolve_data = {
            'app_label': obj._meta.app_label,
            'model_name': obj._meta.model_name,
            'field_name': self.instance_name,
        }
        resolved_template_names = [
            tn.format(**resolve_data) for tn in template_names
        ]
        t = loader.select_template(resolved_template_names)
        return t.render(Context({'object': obj}))
Ejemplo n.º 9
0
 def __init__(self, label, facet_id, parent_id, **kwargs):
     if label is None:
         raise SearchFieldError("'{0}' fields must have a label." \
                                .format(self.__class__.__name__))
     self.label = label
     self.facet_id = facet_id
     self.parent_id = parent_id
     super(LabeledField, self).__init__(**kwargs)
Ejemplo n.º 10
0
    def resolve_attributes_lookup(self, current_objects, attributes):
        """
        Recursive method that looks, for one or more objects, for an attribute that can be multiple
        objects (relations) deep.
        """
        values = []

        for current_object in current_objects:
            if not hasattr(current_object, attributes[0]):
                raise SearchFieldError(
                    "The model '%r' does not have a model_attr '%s'."
                    % (repr(current_object), attributes[0])
                )

            if len(attributes) > 1:
                current_objects_in_attr = self.get_iterable_objects(
                    getattr(current_object, attributes[0])
                )
                values.extend(
                    self.resolve_attributes_lookup(
                        current_objects_in_attr, attributes[1:]
                    )
                )
                continue

            current_object = getattr(current_object, attributes[0])

            if current_object is None:
                if self.has_default():
                    current_object = self._default
                elif self.null:
                    current_object = None
                else:
                    raise SearchFieldError(
                        "The model '%s' combined with model_attr '%s' returned None, but doesn't allow "
                        "a default or null value."
                        % (repr(current_object), self.model_attr)
                    )

            if callable(current_object):
                values.append(current_object)
            else:
                values.append(current_object)

        return values
Ejemplo n.º 11
0
    def __init__(self, **kwargs):
        if kwargs.get('facet_class') is None:
            kwargs['facet_class'] = FacetMultiValueField

        if kwargs.get('use_template') is True:
            raise SearchFieldError("'%s' fields can not use templates to prepare their data." % self.__class__.__name__)

        super(MultiValueField, self).__init__(**kwargs)
        self.is_multivalued = True
 def all_searchfields(self):
     """
     Builds a dictionary of all fields appearing in any of the `SearchIndex`
     instances registered with a site.
     
     This is useful when building a schema for an engine. A dictionary is
     returned, with each key being a fieldname (or index_fieldname) and the
     value being the `SearchField` class assigned to it.
     """
     content_field_name = ''
     fields = {}
     
     for model, index in self.get_indexes().items():
         for field_name, field_object in index.fields.items():
             if field_object.document is True:
                 if content_field_name != '' and content_field_name != field_object.index_fieldname:
                     raise SearchFieldError("All SearchIndex fields with 'document=True' must use the same fieldname.")
                 
                 content_field_name = field_object.index_fieldname
             
             if not field_object.index_fieldname in fields:
                 fields[field_object.index_fieldname] = field_object
                 fields[field_object.index_fieldname] = copy.copy(field_object)
             else:
                 # If the field types are different, we can mostly
                 # safely ignore this. The exception is ``MultiValueField``,
                 # in which case we'll use it instead, copying over the
                 # values.
                 if field_object.is_multivalued == True:
                     old_field = fields[field_object.index_fieldname]
                     fields[field_object.index_fieldname] = field_object
                     fields[field_object.index_fieldname] = copy.copy(field_object)
                     
                     # Switch it so we don't have to dupe the remaining
                     # checks.
                     field_object = old_field
                 
                 # We've already got this field in the list. Ensure that
                 # what we hand back is a superset of all options that
                 # affect the schema.
                 if field_object.indexed is True:
                     fields[field_object.index_fieldname].indexed = True
                 
                 if field_object.stored is True:
                     fields[field_object.index_fieldname].stored = True
                 
                 if field_object.faceted is True:
                     fields[field_object.index_fieldname].faceted = True
                 
                 if field_object.use_template is True:
                     fields[field_object.index_fieldname].use_template = True
                 
                 if field_object.null is True:
                     fields[field_object.index_fieldname].null = True
     
     return fields
Ejemplo n.º 13
0
    def _prepare_template(self, obj, language):
        if self.instance_name is None and self.template_name is None:
            raise SearchFieldError("This field requires either its instance_name variable to be populated or an explicit template_name in order to load the correct template.")

        if self.template_name is not None:
            template_names = self.template_name

            if not isinstance(template_names, (list, tuple)):
                template_names = [template_names]
        else:
            template_names = ['search/indexes/%s/%s_%s.txt' % (obj._meta.app_label, obj._meta.module_name, self.instance_name)]
        t = loader.select_template(template_names)
        return t.render(Context({'object': obj, 'language': language}))
Ejemplo n.º 14
0
    def convert(self, value):
        if value is None:
            return None

        if isinstance(value, basestring):
            match = DATETIME_REGEX.search(value)

            if match:
                data = match.groupdict()
                return datetime_safe.datetime(int(data['year']), int(data['month']), int(data['day']), int(data['hour']), int(data['minute']), int(data['second']))
            else:
                raise SearchFieldError("Datetime provided to '%s' field doesn't appear to be a valid datetime string: '%s'" % (self.instance_name, value))

        return value
Ejemplo n.º 15
0
    def convert(self, value):
        if value is None or value == []:
            return None

        if isinstance(value, six.string_types):
            match = DATE_REGEX.search(value)

            if match:
                data = match.groupdict()
                return datetime_safe.date(int(data['year']), int(data['month']), int(data['day']))
            else:
                raise SearchFieldError("Date provided to '%s' field doesn't appear to be a valid date string: '%s'" % (self.instance_name, value))

        return value
Ejemplo n.º 16
0
    def __init__(self, **kwargs):
        if kwargs.get('facet_class') is None:
            kwargs['facet_class'] = FacetMultiValueField

        if kwargs.get('use_template') is True:
            raise SearchFieldError(
                "'%s' fields can not use templates to prepare their data." %
                self.__class__.__name__)

        if ((5, 0, 0) <= elasticsearch.__version__ <
            (6, 0, 0)) and self.field_type == 'string':
            self.field_type = 'text'

        super(MultiValueField, self).__init__(**kwargs)
        self.is_multivalued = True
    def prepare_template(self, obj):
        """
        Flattens an object for indexing.
        
        This loads a template
        (``search/indexes/{app_label}/{model_name}_{field_name}.txt``) and
        returns the result of rendering that template. ``object`` will be in
        its context.
        """
        if self.instance_name is None and self.template_name is None:
            raise SearchFieldError(
                "This field requires either its instance_name variable to be populated or an explicit template_name in order to load the correct template."
            )

        if self.template_name is not None:
            template_name = self.template_name
        else:
            template_name = 'search/indexes/%s/%s_%s.txt' % (
                obj._meta.app_label, obj._meta.module_name, self.instance_name)

        t = loader.get_template(template_name)
        return t.render(Context({'object': obj}))
Ejemplo n.º 18
0
    def convert(self, value):
        if value is None:
            return None

        if isinstance(value, six.string_types):
            match = DATETIME_REGEX.search(value)

            if match:
                data = match.groupdict()
                return datetime_safe.datetime(
                    int(data["year"]),
                    int(data["month"]),
                    int(data["day"]),
                    int(data["hour"]),
                    int(data["minute"]),
                    int(data["second"]),
                )
            else:
                raise SearchFieldError(
                    "Datetime provided to '%s' field doesn't appear to be a valid datetime string: '%s'"
                    % (self.instance_name, value))

        return value
Ejemplo n.º 19
0
    def __init__(self, **kwargs):
        if kwargs.get('faceted') is True:
            raise SearchFieldError("%s can not be faceted." % self.__class__.__name__)

        super(NgramField, self).__init__(**kwargs)