Example #1
0
 def _update_obj_index_iter(self, obj):
     """Either updates the given object index, or yields an unsaved search entry."""
     model = obj.__class__
     adapter = self.get_adapter(model)
     content_type = ContentType.objects.get_for_model(model)
     object_id = force_text(obj.pk)
     # Create the search entry data.
     search_entry_data = {
         "engine_slug": self._engine_slug,
         "title": adapter.get_title(obj),
         "description": adapter.get_description(obj),
         "content": adapter.get_content(obj),
         "url": adapter.get_url(obj),
         "meta_encoded": json.dumps(adapter.get_meta(obj)),
     }
     # Try to get the existing search entry.
     object_id_int, search_entries = self._get_entries_for_obj(obj)
     # Attempt to update the search entries.
     update_count = search_entries.update(**search_entry_data)
     if update_count == 0:
         # This is the first time the entry was created.
         search_entry_data.update((
             ("content_type", content_type),
             ("object_id", object_id),
             ("object_id_int", object_id_int),
         ))
         yield SearchEntry(**search_entry_data)
     elif update_count > 1:
         # Oh no! Somehow we've got duplicated search entries!
         search_entries.exclude(id=search_entries[0].id).delete()
Example #2
0
    def get_title(self, obj):
        """
        Returns the title of this search result. This is given high priority in search result ranking.

        You can access the title of the search entry as `entry.title` in your search results.

        The default implementation returns `force_text(obj)`.
        """
        return force_text(obj)
Example #3
0
 def testSiteSearchJSON(self):
     # Test a search that should find everything.
     response = self.client.get("/simple/json/?q=title")
     self.assertEqual(response["Content-Type"], "application/json; charset=utf-8")
     results = set(result["title"] for result in json.loads(force_text(response.content))["results"])
     self.assertEqual(len(results), 4)
     self.assertTrue("title model1 instance11" in results)
     self.assertTrue("title model1 instance12" in results)
     self.assertTrue("title model2 instance21" in results)
     self.assertTrue("title model2 instance22" in results)
Example #4
0
 def _resolve_field(self, obj, name):
     """Resolves the content of the given model field."""
     name_parts = name.split("__", 1)
     prefix = name_parts[0]
     # If we're at the end of the resolve chain, return.
     if obj is None:
         return ""
     # Try to get the attribute from the object.
     try:
         value = getattr(obj, prefix)
     except ObjectDoesNotExist:
         return ""
     except AttributeError:
         # Try to get the attribute from the search adapter.
         try:
             value = getattr(self, prefix)
         except AttributeError:
             raise SearchAdapterError("Could not find a property called {name!r} on either {obj!r} or {search_adapter!r}".format(
                 name = prefix,
                 obj = obj,
                 search_adapter = self,
             ))
         else:
             # Run the attribute on the search adapter, if it's callable.
             if not isinstance(value, (QuerySet, models.Manager)):
                 if callable(value):
                     value = value(obj)
     else:
         # Run the attribute on the object, if it's callable.
         if not isinstance(value, (QuerySet, models.Manager)):
             if callable(value):
                 value = value()
     # Look up recursive fields.
     if len(name_parts) == 2:
         if isinstance(value, (QuerySet, models.Manager)):
             return " ".join(force_text(self._resolve_field(obj, name_parts[1])) for obj in value.all())
         return self._resolve_field(value, name_parts[1])
     # Resolve querysets.
     if isinstance(value, (QuerySet, models.Manager)):
         value = " ".join(force_text(related) for related in value.all())
     # Resolution complete!
     return value
Example #5
0
 def testSiteSearchCustomJSON(self):
     # Test a search that should find everything.
     response = self.client.get("/custom/json/?fooo=title&page=last")
     self.assertEqual(response["Content-Type"], "application/json; charset=utf-8")
     results = set(result["title"] for result in json.loads(force_text(response.content))["results"])
     self.assertEqual(len(results), 4)
     self.assertTrue("title model1 instance11" in results)
     self.assertTrue("title model1 instance12" in results)
     self.assertTrue("title model2 instance21" in results)
     self.assertTrue("title model2 instance22" in results)
     # Test a search with an invalid page.
     response = self.client.get("/custom/json/?fooo=title&page=200")
     self.assertEqual(response.status_code, 404)
Example #6
0
    def get_content(self, obj):
        """
        Returns the content of this search result. This is given low priority in search result ranking.

        You can access the content of the search entry as `entry.content` in your search results, although
        this field generally contains a big mess of search data so is less suitable for frontend display.

        The default implementation returns all the registered fields in your model joined together.
        """
        # Get the field names to look up.
        field_names = self.fields or (field.name for field in self.model._meta.fields if isinstance(field, (models.CharField, models.TextField)))
        # Exclude named fields.
        field_names = (field_name for field_name in field_names if field_name not in self.exclude)
        # Create the text.
        return self.prepare_content(" ".join(
            force_text(self._resolve_field(obj, field_name))
            for field_name in field_names
        ))
Example #7
0
def resolve_url(to, *args, **kwargs):
    """
    Return a URL appropriate for the arguments passed.
    The arguments could be:
        * A model: the model's `get_absolute_url()` function will be called.
        * A view name, possibly with arguments: `urlresolvers.reverse()` will
          be used to reverse-resolve the name.
        * A URL, which will be returned as-is.
    """
    from compat import six, force_text

    # If it's a model, use get_absolute_url()
    if hasattr(to, 'get_absolute_url'):
        return to.get_absolute_url()

    if isinstance(to, Promise):
        # Expand the lazy instance, as it can cause issues when it is passed
        # further to some Python functions like urlparse.
        to = force_text(to)

    if isinstance(to, six.string_types):
        # Handle relative URLs
        if any(to.startswith(path) for path in ('./', '../')):
            return to

    # Next try a reverse URL resolution.
    try:
        return urlresolvers.reverse(to, args=args, kwargs=kwargs)
    except urlresolvers.NoReverseMatch:
        # If this is a callable, re-raise.
        if callable(to):
            raise
        # If this doesn't "feel" like a URL, re-raise.
        if '/' not in to and '.' not in to:
            raise

    # Finally, fall back and assume it's a URL
    return to
Example #8
0
 def _get_entries_for_obj(self, obj):
     """Returns a queryset of entries associate with the given obj."""
     model = obj.__class__
     content_type = ContentType.objects.get_for_model(model)
     object_id = force_text(obj.pk)
     # Get the basic list of search entries.
     search_entries = SearchEntry.objects.filter(
         content_type = content_type,
         engine_slug = self._engine_slug,
     )
     if has_int_pk(model):
         # Do a fast indexed lookup.
         object_id_int = int(obj.pk)
         search_entries = search_entries.filter(
             object_id_int = object_id_int,
         )
     else:
         # Alas, have to do a slow unindexed lookup.
         object_id_int = None
         search_entries = search_entries.filter(
             object_id = object_id,
         )
     return object_id_int, search_entries
Example #9
0
def escape_query(text):
    text = force_text(text)
    text = RE_SPACE.sub(" ", text)  # Standardize spacing.
    text = RE_NON_WORD.sub("", text)  # Remove non-word characters.
    return text
Example #10
0
 def __str__(self):
     return force_text(self.title)