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