##################
# models.py
##################

from search import SearchQuerySet
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

# Make sure to create this fulltext index!
# create fulltext index searchable_media on searchable_searchablemedia (search_text);
#
class SearchableMediaManager(models.Manager):
    def search(self,query):
        return SearchQuerySet(self.model,['search_text']).search(query)

class SearchableMedia(models.Model):
    objects=SearchableMediaManager()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type','object_id')
    search_text = models.TextField()

    def __str__(self):
        return "Searchable Content for %s-%s"%(self.content_type.name,self.get_content_object())

    def get_content_object(self):
        return self.content_object

    # for debug for now
    class Admin:
        pass


def update_terms(object,terms):
    sm=None
    try:
        sm=SearchableMedia.objects.get(content_type=ContentType.objects.get_for_model(object),object_id=object.id)
    except SearchableMedia.DoesNotExist:
        sm=SearchableMedia(content_type=ContentType.objects.get_for_model(object),object_id=object.id)
    if sm:
        sm.search_text=terms
        sm.save()

##################
# views.py
##################

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from models import SearchableMedia

# Create your views here.

def search(request,template="search.html"):
    if "keyword" not in request.REQUEST:
        return HttpResponseRedirect("/")
    results=SearchableMedia.objects.search(request.REQUEST["keyword"])
    return render_to_response(template,{"results":results},context_instance=RequestContext(request))

##################
# search.py
##################

from django.db import models, backend

class SearchQuerySet(models.query.QuerySet):
    """ Search Query Set courtesy of http://www.mercurytide.com/whitepapers/django-full-text-search/
    """
    def __init__(self, model=None, fields=None):
        super(SearchQuerySet, self).__init__(model)
        self._search_fields = fields

    def search(self, query):
        meta = self.model._meta
        db_operations = backend.DatabaseOperations()

        # Get the table name and column names from the model
        # in `table_name`.`column_name` style
        columns = [meta.get_field(name,
                                  many_to_many=False).column
            for name in self._search_fields]
        full_names = ["%s.%s" %
                (db_operations.quote_name(meta.db_table),
                 db_operations.quote_name(column))
            for column in columns]

        # Create the MATCH AGAINST expressions        
        fulltext_columns = ", ".join(full_names)
        match_expr = ("MATCH(%s) AGAINST (%%s)" %
                      fulltext_columns)

        # Add the extra SELECT and WHERE options
        return self.extra(select={'relevance': match_expr},
                          where=[match_expr],
                          params=[query, query])

##################
# models.py of objects you want included in search
##################

def post_blogentry_save(sender,instance):
    update_terms(instance," ".join([instance.title,instance.body]))
dispatcher.connect(post_blogentry_save, sender=BlogEntry, signal=models.signals.post_save)

def post_video_save(sender,instance):
    update_terms(instance," ".join([instance.title,instance.body]))
dispatcher.connect(post_video_save, sender=Video, signal=models.signals.post_save)

##################
# Template
##################

{% extends "base.html" %}

{% block content %}
<div id="searchresults">
   {% for o in results %}
     <div class="searchresult">

        {% ifequal o.content_type.name "video" %}
        {% with o.content_object as video %}
        <!-- Video search result here -->
        {% endwith %}
        {% endifequal %}

        {% ifequal o.content_type.name "blogentry" %}
        {% with o.content_object as blogentry %}
        <!-- BlogEntry search result here -->
        {% endwith %}
        {% endifequal %}

     </div>
  {% endfor %}

</div>
{% endblock %}