Latest Entries RSS

Exploring a Pygresql Backend

April 16, 2008

I was running into an issue with Psycopg2 and Decimal where you couldn't add a Decimal value on your model object to a regular non-model-object Decimal value [1] [2].

While I was waiting for the initd.org site to come back up so I could download the latest psycopg2 release to fix the problem, I decided to see how hard it would be integrate PyGreSQL into the backend system.

I decided to use the pgdb API as it's DB-API 2.0 compliant. It turns out that in about an hour I was able to digest the django.db.backend code and get a postgresql_pgdb backend working.

Most of my efforts leveraged the existing postgres backends provided by Django, so I really didn't generate a lot of new code.

Where I ran into problems was with the typing provided by the pgdb package. The database type NUMERIC is returned as a float and dates and times are returned as strings.

Still waiting for the initd site to come back up, I went ahead and poked my nose into the pgdb.py module and found it pretty easy to enhance it's casting of types (it doesn't support type extensions like PsycoPG). By the time initd came back up I had the pgdb module returning decimals, datetimes, dates, and times.

All this was pretty exciting, but once I got a hold of the latest Psycopg2 release, a quick download, build, and install fixed my original problem. And so the PyGreSQL code now sits as 'psuedo-abandoned' in my /usr/local/src/django/trunk directory.

The question left in my head is the following: Would a PyGreSQL backend be a worthwhile endeavor? Since it was just a hop, skip, and a jump to get the thing 80% functional (based on the 80/20 rule), would it be helpful to have another Postgres backend?

I'm a relative newbie to the Python world, so I don't know the cultural status of PyGreSQL vs. PsycoPG, and whether or not PyGreSQL is a serious contender for Postgres connectivity market share...

Anyway, I thought I would share this experience as I found it enlightening. Thanks for reading...

doug.

Post Script:

I ran into this issue while working on a client website who sells rolex watches. He asked for a little link love. :-)

[1]django-users discussion: unsupported operand type(s) for *: 'Decimal' and 'Decimal'
[2]django-developers discussion: DecimalField and 'decimal' module.

The Test Client Session

January 23, 2008

I just ran into a funny little quirk when writing a unit test in Django.

I had the following code:

data = {} # my data omitted
self.client.session['product_response'] = {
    
'message': 'Test message.'
    
}
self.client.session.modified = True
self.client.session.save()
response = self.client.post('/foo/', data)

When running this test it failed because request.session[ 'product_response' ] wasn't a valid key. I dropped into a shell and discovered:

>>> client.session['foo'] = 1
>>> 'foo' in client.session
False
>>> client.session is client.session
False

So that had me puzzled. After fiddling around, I found that I could get things to work right if I grabbed the session as a local variable and worked against that.

So my test code ended up looking like this:

data = {} # my data omitted
s = self.client.session
s['product_response'] = {
    
'message': 'Test message.'
    
}
s.modified = True
s.save()
response = self.client.post('/foo/', data)

And that did the trick.

I haven't explored why this is the case, but it was interesting enough to me that I thought I'd share it.

doug.

Dynamic Upload To

October 11, 2007

I recently ran into a problem in my application where I needed to save an uploaded document to a directory based on another field in my model. Since FileField only works with statically declared directories via upload_to, I needed something different.

The Problem

Given the following model:

class Document(models.Model):
    
org = models.ForeignKey(Organization)
    
document = models.FileField(upload_to="documents")

How could I get the document field to change its upload_to attribute each time the save_document_file() method is called?

The Research

In order to answer this question, I needed to figure out what happens when save_document_file() is called on an instance of my model (since it's this method that actually saves the file to disk). I poked around the base.py and fields/__init__.py modules under django.db.models to try and figure things out. Since I'm no Python guru I was left scratching my head. So off to Google I went. After a moderate search effort I came across the following gem:

Model Creation and Initialisation

This document is an excellent read and will help you understand how Django Models work. Much thanks to Malcolm Treddnick. I recommend reading it as soon as your boss isn't looking. ;-) In that document I learned the following key point about Django Models:

"The important thing to realise is that they [model fields] are not added as attributes on the main class, but, rather, they are stored in the _meta attribute and will be called upon at save or load or delete time."

Which is to say that the document attribute is not per instance, but rather per class. So changing the upload_to attribute on the document field was not an option (that would change class behavior), nor was accessing model instance attributes from the document field (a class attribute knows nothing of the particular instance in play).

Enter _save_FIELD_file()

With my new understanding of how things were working under the Model's covers, I took a closer look at _save_FIELD_file(). This Model method is what ends up getting called when save_document_file() is called. One of the things passed to it is the FileField being saved, in my case document. And that's where I saw my chance.

After some fiddling and false starts, I ended up with the following:

def _save_FIELD_file(self, field, filename, raw_contents, save=True):
    
# construct a temporary copy with the new upload to directory.
    
temp_field = field
    
if self.org:
        
temp_field = models.FileField()
        
temp_field.__dict__.update(field.__dict__)
        
temp_field.upload_to = '%s/%s' % (field.upload_to, self.org.name)
    
super(Document, self)._save_FIELD_file(temp_field, filename, raw_contents, save=True)
_save_FIELD_file.alters_data = True

In this method override I make a copy of the supplied, class owned field, modify it's upload_to attribute, and pass that along to the parent _save_FIELD_file() method.

Now, when I call doc.save_document_file(), the organization name will be appended to the document directory, placing uploaded organization documents in their own directories.

Now I'm no Python expert, so I'm not completely sure of the line temp_field.__dict__.update(field.__dict__). Without a fuller understanding of the language I fear I may be making some fundamentally flawed assumptions there. But, from what I do know, it seems like it should work. Well, it does work, so it seems like it should be safe.

In Summary

This solution is my hack to get past my immediate problem. It's not designed for reuse nor do I advocate it as the right, or Pythonic, way to handle the problem.

Also, the problem has been discussed by on the django-users mailing list [1] [2], and it has at least two patches addressing the problem [3] [4].

However, it works for me, and since I learned a few things along the way I thought I'd share it with the community.

Thanks for reading!

[1]django-users discussion: Context-sensitive 'upload_to' parameter for FileField
[2]django-users discussion: FileField - my variable used in "upload_to" parameter
[3]#1994: [patch] Custom upload_to per instance of FileField
[4]#5361: Support pluggable backends for FileField

Flushing Memcached

October 11, 2007

In getting my blog off the ground, going through the 'minor-update, publish, release' cycle, I found myself needing to flush memcached to see my changes on my production server (I didn't want to wait 15 minutes for the cache to expire).

A quick google yielded the following technique:

$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
flush_all
OK
quit
Connection closed by foreign host.

I thought that was pretty helpful.

Introduction

October 10, 2007

Hi. My name is Doug Van Horn and this is my blog. I've been building websites since 1996 and I've recently decided to start a business. I plan on using this blog to share my experiences in this endeavor.

The idea of a blog doesn't come naturally to me. I'm very much an introvert. So I imagine my entries here will be very business-like and to the point. I don't see myself going off on long, personal rants about subjects that don't pertain to my business or the technology I use.

Speaking of technology, I have been using Java for the past 10 years. I was there when JSPs hit the scene and when Struts was all the rage. About a year and a half ago I finally learned myself a new language, Python, and I've not looked back.

Django is my web framework of choice. It's what this blog is built upon. It's what my company will use to build it's products and it's customers websites. I tried Ruby and Rails, but it wasn't for me. Simply put, the Ruby the language didn't fit me, at least not like Python.

So that's a bit about me and the intent of this blog. Hopefully I'll be able to document some things that others will find interesting and worth reading. Otherwise this will pretty much be like talking to myself, which isn't so bad because I do so love good conversation [1].

[1]From the mighty George Carlin.