Category: Web Development


Django Custom Model Manager Chaining

Let’s say you have a custom model manager like this:

from datetime import datetime

from django.db import models

class PostManager(models.Manager):
    def by_author(self, user):
        return self.filter(user=user)

    def published(self):
        return self.filter(published__lte=datetime.now())

class Post(models.Model):
    user = models.ForeignKey(User)
    published = models.DateTimeField()

    objects = PostManager()

But let’s say you are want to filter those results that are both published and by a certain author. There is no built-in mechanism that will allow you to chain the two together.

So for example, the following will fail since by_author returns a QuerySet:

Post.objects.by_author(user=request.user).published()

There are a few discussions on the matter:

But using Python mixins I’ve developed what I think to be a better way:

from django.db.models.query import QuerySet

class PostMixin(object):
    def by_author(self, user):
        return self.filter(user=user)

    def published(self):
        return self.filter(published__lte=datetime.now())

class PostQuerySet(QuerySet, PostMixin):
    pass

class PostManager(models.Manager, PostMixin):
    def get_query_set(self):
        return PostQuerySet(self.model, using=self._db)

It keeps with DRY principles and works just like you expect Django’s ORM to work.

GoDaddy SSL Certificate and Chrome

On one of my recent projects, I was getting a message from Google Chrome saying: “The site’s security certificate is not trusted!”

It was was a valid certificate in all other major browsers. And a few different computers running Chrome had no issues. So what gives?

Through some Googling, I learned this:

This could be because the SSL provider is using a new Root certificate that isn’t included in the old browsers and devices. The error can usually be fixed by installing an Intermediate certificate that will link the new Root certificate to an old trusted certificate. –Robert [SSL Certificate Not Trusted Error]

If your curious to learn more about intermediate certificates, GoDaddy has a decent explanation: What is an intermediate certificate?

How to Fix

You need to download a GoDaddy Secure Server Certificate (Intermediate Certificate): gd_intermediate.crt [GoDaddy Repository]

For Apache, you need to add the following to your configuration:

SSLCertificateChainFile /etc/certs/gd_intermediate.crt

For Nginx, there’s a little more work to do the same thing. You need to concatenate your existing certificate file with the intermediate certificate to produce a new certificate file, which you will as your new certificate.

cat example.com.crt gd_intermediate.crt > example.com-combined.crt

Then in your nginx configuration file you can change

ssl_certificate      /etc/certs/example.com.crt;

to

ssl_certificate      /etc/certs/example.com-combined.crt;

Save the following as /System/Library/LaunchDaemons/org.mongo.mongod.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.mongo.mongod</string>
	<key>RunAtLoad</key>
	<true/>
	<key>ProgramArguments</key>
	<array>
		<string>/usr/local/bin/mongod</string>
		<string>--dbpath</string>
		<string>/var/lib/mongodb/</string>
		<string>--logpath</string>
		<string>/var/log/mongodb.log</string>
	</array>
</dict>
</plist>

You will need to create a file for the log and a directory for the database.

sudo touch /var/log/mongodb.log
sudo mkdir /var/lib/mongodb

And in a bash session, run the following:

sudo chown root:wheel /System/Library/LaunchDaemons/org.mongo.mongod.plist
sudo launchctl load /System/Library/LaunchDaemons/org.mongo.mongod.plist
sudo launchctl start org.mongo.mongod

Django Messaging for AJAX Calls Using Jquery

The messaging contrib app for Django has always been been tied to a user, which has prevented me from using it in any of my apps. Now that the Django 1.2 Alpha has been released, I’ve been able to play with it, and feel good about using it. My sites usually have a mix of AJAX and traditional HTTP requests. So figuring out a good solution to handle messages for the AJAX requests, while maintaining consistency in interacting with the API was important to me. Here’s my solution for handling messages in AJAX requests.

First we need a middleware that will detect if the request is an AJAX request. I always use JSON, but if you mix the data types, you might have to add some additional logic here. The middleware adds to the JSON any messages that we might have added in the view.

import simplejson as json

from django.contrib import messages

class AjaxMessaging(object):
    def process_response(self, request, response):
        if request.is_ajax():
            if response['Content-Type'] in ["application/javascript", "application/json"]:
                try:
                    content = json.loads(response.content)
                except ValueError:
                    return response

                django_messages = []

                for message in messages.get_messages(request):
                    django_messages.append({
                        "level": message.level,
                        "message": message.message,
                        "extra_tags": message.tags,
                    })

                content['django_messages'] = django_messages

                response.content = json.dumps(content)
        return response

Make sure to add the new middleware to settings.py.

'apps.main.middleware.AjaxMessaging'

So if we have a view that responds to an AJAX request, we can add a message the same way we would for a traditional HTTP response:

if success:
    messages.success(request, "The object has been modified.")
else:
    messages.error(request, "The object was not modified.")

We need a place to put the messages in our template:

<ul id="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
    {% endfor %}
</ul>

We’ll be using a global ajaxComplete handler, so we’ll have to use a JSON parser. Simply include it with the rest of our javascript. jquery.json.js

And finally the handler, which simply loops through all the Django messages, and fades them out after three seconds.

function addMessage(text, extra_tags) {
    var message = $('<li class="'+extra_tags+'">'+text+'</li>').hide();
    $("#messages").append(message);
    message.fadeIn(500);

    setTimeout(function() {
        message.fadeOut(500, function() {
            message.remove();
        });
    }, 3000);
}

$(document).ready(function() {
    $('#messages').ajaxComplete(function(e, xhr, settings) {
        var contentType = xhr.getResponseHeader("Content-Type");

        if (contentType == "application/javascript" || contentType == "application/json") {
            var json = $.evalJSON(xhr.responseText);

            $.each(json.django_messages, function (i, item) {
                addMessage(item.message, item.extra_tags);
            });
        }
    }).ajaxError(function(e, xhr, settings, exception) {
        addMessage("There was an error processing your request, please try again.", "error");
    });

How to Handle HTTP Redirects with Jquery and Django

Jquery has a funny way of handling 302 redirects during AJAX calls. These frequently come up when I need to redirect a user to a login page. Here’s how I’m handling it with Django.

The underlying problem we experience with redirects in Jquery is that the redirects are followed. So instead of getting a status code of HTTP/1.1 302 Found, we get HTTP/1.1 200 OK. So one way to get around this is to return a made up status code. This is easily accomplished with a middleware in django.

from django.http import HttpResponseRedirect

class AjaxRedirect(object):
    def process_response(self, request, response):
        if request.is_ajax():
            if type(response) == HttpResponseRedirect:
                response.status_code = 278
        return response

Make sure to add the new middleware to settings.py.

'apps.main.middleware.AjaxRedirect'

And the final piece of the puzzle is to add a global AJAX complete handler, that checks for the new status code, redirects the page to the Location header in the HTTP response, and replace the next query variable. The last part is important because the login_required decorator automatically sets the next query variable to the HTTP Referrer, which in this case would be the URL of the AJAX call. So we want to replace it with the page we’re currently on.

$(document).ready(function() {
    $('body').ajaxComplete(function(e, xhr, settings) {
        if (xhr.status == 278) {
            window.location.href = xhr.getResponseHeader("Location").replace(/\?.*$/, "?next="+window.location.pathname);
        }
    });
});
  1. Download and install MySQL Package file [MySQL 5.1 for 10.5 (x86_64)]
  2. Install MySQL Startup Item [Howto]
  3. Turn on Web Sharing in System Preferences -> Sharing
    Screen shot 2009-11-29 at 5.19.43 PM
  4. Copy /etc/php.ini.default to /etc/php.ini
    sudo cp /etc/php.ini.default /etc/php.ini
  5. Add timezone information to php.ini (PHP will error without this)
    date.timezone = 'America/New_York'
  6. Install php-mcrypt
    1. Download libmcrypt 2.5.8
    2. Build and install .
      ./configure --disable-posix-threads --enable-dynamic-loading
      make
      sudo make install
    3. Download PHP 5.3.1
    4. Navigate to php-5.3.1/ext/mcrypt/
    5. Build and install .
      phpize
      ./configure
      make
      sudo make install
    6. Add extension to php.ini
      extension=mcrypt.so
  7. Install APC
    1. Download PCRE 8.0
    2. Build and install .
      ./configure
      make
      sudo make install
    3. Download APC 3.1.3p1
    4. Navigate to APC-3.1.3p1/APC-3.1.3p1/
    5. Build and install .
      phpize
      ./configure
      make
      sudo make install
    6. Add extension to php.ini
      extension=apc.so
  8. Download and install MySQL-python 1.2.3c1
    sudo python setup.py install
  9. Download and install libjpeg.v7
    ./configure --enable-shared
    make
    sudo make install
  10. Download and install Python Imaging Library 1.1.6
    sudo python setup.py install
Powered by WordPress | Theme: Motion by 85ideas.