Login

Searchable Media

Author:
nikolaj
Posted:
March 21, 2008
Language:
Python
Version:
.96
Score:
2 (after 2 ratings)

This app allows you to utilize mysql's fulltext searching over multiple models and multiple apps, letting the site search seem more intuitive, yet still allow your content to be very structured. Essentially, it creates an entire new model that associates objects to a chunk of text to search efficiently over (and index), using the contenttypes app. Simply add the post_save events to your existing models for things you want to be searched.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
##################
# 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 %}

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.