Stored Procedures in Django

Web development platforms like Django go a long way to removing the need for writing hand-crafted SQL with all of their ORM goodness. Sometimes, for some reason or another, it's handy to execute hand-crafted SQL code or even a stored procedure. Naturally that should be a last resort and I don't intend on starting a discussion about when it is and when is not appropriate, but for those situations where it is this article applies.

Database

To illustrate how to do this I'll start out with the following MySQL table (the remainder of the post assumes it's filled with sample data). It'll sit underneath the the Document class in an application named "searcher". I'm sort of working backwards here as I've actually generated this table from a model I've included later in the article.

CREATE TABLE searcher_document (
    id integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    url varchar(900) NOT NULL,
    content longtext NOT NULL,
    title longtext NOT NULL
);

Then, just to give us something fun to do, I'll slap a fulltext index on the content and title columns:

CREATE FULLTEXT INDEX FIX ON searcher_document(content, title);

Now I need a stored procedure to call. Here's a simple procedure that performs a fulltext query on the table I just created. In reality I'd probably only employ a procedure for a somewhat more complex task but it'll work for demonstrative purposes.

CREATE PROCEDURE searcher_document_search(
	search_string varchar(255)
) 
BEGIN
	SELECT id, url, content, title 
	FROM searcher_document 
	WHERE MATCH(content, title) AGAINST (search_string);
END

Model

With the database work out of the way I'm ready to define my model:

from django.db import models
from django.db import connection

# Create your models here.

class Document(models.Model):
    # fields
    url = models.CharField(max_length=900)
    content = models.TextField()
    title = models.TextField()

    # static method to perform a fulltext search
    @staticmethod
    def search(search_string):
        # create a cursor
        cur = connection.cursor()
        # execute the stored procedure passing in 
        # search_string as a parameter
        cur.callproc('searcher_document_search', [search_string,])
        # grab the results
        results = cur.fetchall()
        cur.close()

        # wrap the results up into Document domain objects 
        return [Document(*row) for row in results]

And that's about it, the comments tell the tale. I created a cursor and employed its callproc method to execute my procedure. Then I wrapped the results up into a list of Document objects.

Execution

With the business end of this out of the way I can now perform searches in a view as follows:

results = Document.search(request.GET['search_string'])

Conclusion

Like I said before, when developing web applications in ORM based frameworks such as Django there's almost always a way to avoid hand-crafting SQL or using stored procedures. In those situations where it can't be avoided or is inadvisable it's nice to know that you still have the option.

Created on 2009-08-09 18:23:37
Share on Facebook Facebook
Comment Feed
Do you have an example of where you can't avoid stored procs? I can't think of any except when your using a shared-db legacy application, which is not the case when your using Django. 
by Paco on 2009-08-10 14:01:11
Darn close.  In my case I'm conditionally performing some write operations via other procedures (essentially logging and transaction tracking) in a third-party second database 

/* if some condition is met */
call OtherDatabase.LogSearch(...);

and conditionally union-ing my query results with a table in that second database;  

/* if some condition is met */
select title, url from searcher_document 
union
select title, url from OtherDatabase.documents;

It's a definite edge-case.  
by chrisumbel on 2009-08-10 16:19:19
Add a Comment: (HTML not accepted. URLs will automatically be converted to links)
Body
Nickname (Login || Register)
Home Page
Email Addy(kept private)
Are you human?
Tags:
linq .net performance sql 2008 sql server powershell indexes scripting reporting services filestream ruby ironruby entity framework EF testing .net framework 4.0 ADO.NET SSRS rs setpolicies vb cte c# podcasts webdav exchange server data warehousing Data Services Web Services Astoria jQuery database object oriented cql refactoring remoting simpledb cloud HTML GObject GNOME Vala BI couchdb django ORM python erlang functional C curl stackless concurrency Groovy Java JVM dynamic tools windows ironpython dlr systems programming go CAPTCHA appengine natural language full-text rails lucene wave clr parallel virtualization Oracle iPhone xml Objective-C security cocoa touch Solr lisp VS 2010
Blog History:
Basic Authentication with a NSURLRequest in Cocoa Touch - 01/24/2010
Asynchronous Programming in Cocoa Touch - 01/17/2010
NSXML-like XPath Support in Cocoa Touch with TouchXML - 01/03/2010
Using Solr in Django for Full-Text Searching via Solango - 01/01/2010
Using Entity Framework with Oracle - 12/22/2009
Solutions to Common VirtualBox Problems - 12/20/2009
Parallel Programming with the Task Parallel Library and PLINQ in .Net 4.0 - 12/14/2009
Clojure, A Lisp for the JVM and CLR - 12/13/2009
Google Wave Robots in Java - 12/07/2009
Employing Solr/Lucene with SQL Server for Full-Text Searching - 12/05/2009
Full-Text Indexing in Ruby Using Ferret - 11/28/2009
Home-Brewing a Full-Text Search in Google's AppEngine - 11/22/2009
Using reCAPTCHA With Django - 11/21/2009
Phat Go Code Launched - 11/19/2009
A Little More of Google's Go - 11/17/2009
First Impressions of Go, Google's New Systems Language - 11/14/2009
Scripting Your .Net Applications with IronPython - 11/03/2009
Windows Services in Python - 11/02/2009
My Tool List - 10/26/2009
Groovy: Dynamic Language for the JVM... Groovy! - 10/23/2009
Easy Concurrency with Stackless Python - 10/03/2009
C from erlang via linked-in driver - 09/16/2009
Templating with NDjango - 09/06/2009
A little bit o' Erlang - 08/23/2009
Tale of a website, from rails to asp.net to django - 08/20/2009
Now in Django - 08/19/2009
Stored Procedures in Django - 08/09/2009
CouchDBExtension - 08/06/2009
POCO Entities in ADO.NET 4.0 - 07/30/2009
Accessing SimpleDB from SSRS - 07/22/2009
Easy GNOME Development with the Vala Programming Language - 07/16/2009
HTML Parsing with Ruby and Nokogiri - 07/12/2009
Amazon SimpleDB Batched PUTs Usage and Performance - 07/10/2009
PowerShell 2.0 Out-GridView, ISE and ScriptCmdlets - 07/05/2009
Asynchronous and remote execution with powershell 2 ctp3 - 06/30/2009
Understanding Source Code with NDepend and CQL - 06/22/2009
Object Oriented Databases with db4o - 06/07/2009
ADO.Net Data Services with jQuery - 05/29/2009
Exchange webdav automation - 05/26/2009
Podcasts - 05/26/2009
Linq to Object Performance - 05/11/2009
SQL 2008 and powershell - 01/25/2009
SQL 2008 filtered indexes - 06/11/2008
SQL 2008's table valued parameters - 05/11/2008
SQL 2008's MERGE statement - 04/22/2008
ironruby - 04/11/2008
SSRS scripting with RS.EXE - 11/20/2007
SQL 2008 FILESTREAM - 08/04/2007
CTE Concatenation - 01/01/2007