Fun with Django ManyToMany Fields

This is just another reason I find Django so easy.

For example.. Say you are making a monitoring application. Now you have a bunch of servers and you have certain groups of servers. A server can belong in more then one group also. So that is pretty easy and all with Django.

Now in your template you have a few pages. One page that displays the group information like all the servers in the group and another that displays the server information and will list all the groups that server is in. So below is my basic model layout

class Group(models.Model):
    parent = models.ForeignKey("Group", null=True, blank=True)
    user = models.ForeignKey(User)

    name = models.CharField(max_length=100)
    about = models.TextField(blank=True, null=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.name

class Server(models.Model):
    hostname = models.CharField(max_length=300, db_index=True)
    about = models.TextField(blank=True, null=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    groups = models.ManyToManyField(Group)

    def __unicode__(self):
        return self.hostname

So to get all the groups my server is a member of is pretty easy.

s = Server.objects.get(id=1)
s.groups.all()

Now to get all the servers in a group is also easy

g = Group.objects.get(id=1)
g.server_set.all()

Get Django-NonRel working with VirtualEnv

MongoDB is getting a lot of press recently. I stumbled across a pretty interesting project called Django-nonrel. This is an attempt to build non-relational databases right into mongodb. Before you could always use a 3rd party ORM or just use pymongo right in Django but then you were missing out on all the nice apps the community has created. So the goal of this project is to make it so you can keep on using those. Now as of writing this joins are not working but from what I know they are working on it.

So lets start off by creating a virtualenv environment. I will call my project photoblog

virtualenv photoblog

Now lets activate our virtualenv

source ./photoblog/bin/activate

Now we can install Django-nonrel

wget http://bitbucket.org/wkornewald/django-nonrel/get/tip.zip
unzip tip.zip
cd django-nonrel
python setup.py build
python setup.py install

Now lets install pymongo

pip install pymongo

Now lets install the djangotoolbox

wget http://bitbucket.org/wkornewald/djangotoolbox/get/tip.zip
unzip tip.zip
cd djangotoolbox
python setup.py build
python setup.py install

Now we have to download and install the django-mongo-engine

wget --no-check-certificate https://github.com/aparo/django-mongodb-engine/zipball/master
unzip aparo-django-mongodb-engine*
cd aparo-django-mongodb-engine*
python setup.py build
python setup.py install

So now lets create the Django project

cd ~/photoblog
django-admin.py startproject photoblog
cd photoblog

Now the only thing you really have to change to get a basic site up and running is the following file

settings.py

So edit that and find the block that looks like

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': '',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

And you want it to look something like

DATABASES = {
    'default': {
        'ENGINE': 'django_mongodb_engine.mongodb', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'photoblog',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '27017',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Then you want to add the following to your INSTALLED_APPS

djangotoolbox

Now you can sync the db to set it all up!

cd ~/photoblog/photoblog
chmod +x manage.py
./manage.py syncdb

You are all set!

WSGI Holygrail run multiple versions of Python on same webserver

One issue hosting providers have is the issue with clients needing to run different version of python. One client might need python 2.4 and others might need 2.5. So in comes uWSGI

So this blog post will go over how to install uWSGI on RedHat 5 and making a uWSGI server for both python 2.4 and 2.5 at the same time for different hosts.

So the first thing you want to do is download the source

wget http://projects.unbit.it/downloads/uwsgi-0.9.6.tar.gz
tar zxfv uwsgi-0.9.6.tar.gz
cd uwsgi-0.9.6

Now make the 2.4 version

make -f Makefile.Py24
mv uwsgi uwsgi24

Now make the 2.5 version

make -f Makefile.Py25
mv uwsgi uwsgi25

Now you have two binaries compiled to run 2.4 and 2.5 for python

So now you run the binaries

./uwsgi -s 127.0.0.1:3031 --pythonpath /tmp/multi/ -w django_wsgi

This will run a uWSGI server for a django app that is in /tmp/multi with a django_wsgi.py file.

You can then run

./uwsgi25 -s 127.0.0.1:3032 --pythonpath /tmp/multi/ -w django_wsgi

At the same time and they play nicely.

My django_wsgi.py file looks like

import os
import sys
sys.path.append('/tmp/multi')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Django fix for ‘User’ object has no attribute ‘backend

I was trying to auto login a user if I had their user object and was getting this error when I just called something like

login(request, user)

Well this is due to Django wanting to make sure you auth the user first. If you want to bypass this you can just use the following before login

user.backend = 'django.contrib.auth.backends.ModelBackend'

This might not be the best solution but it works

Reason #4 on why Django is great

Middleware!

I can’t tell you how great middleware is in Django. It makes life so easy! Here is a good example. Django doesn’t handle ajax errors very good. It prints them to the client. Sure I can use firebug but I mainly use chrome now and don’t like to touch firefox anymore. So I wrote a bit of middleware to only print out errors in all ajax requests if the app was in DEBUG mode.


from django.conf import settings

import traceback

class AjaxErrorMiddleware:
    def process_exception(self, request, exception):
        if request.META.has_key('HTTP_X_REQUESTED_WITH') and settings.DEBUG:
            print traceback.format_exc()

I have added this to my Github project also for all my middleware. You just load it by adding it to your middleware section in your settings.py

Reason #3 on why Django is great

Making custom model fields are a great thing to save a lot of time. If you need a lot of custom validation around your modelforms and don’t want to do a lot of copy and pasting then create a custom model field.

For example, I have the need for a user to input a lot of hostnames and domains and got sick of doing custom validation in each model form I created. So I created a model field for it.

So here it is


class HostnameField(models.CharField):
    def clean(self, value):
        value = super(HostnameField, self).clean(value)

        import re
        regex = re.compile("^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$")
        r = regex.search(string)
        if len(r.groups()) == 0:
            raise models.ValidationError("You need to enter a valid hostname/domain")

        return value

The regex might not be the best, but it seems to cover all the use cases I tried on it.

Django: Custom Decorator with an Argument

Sometimes you want to be able to create a decorator for functions in Django but have the ability to pass in an argument into the decorator. I came across this when I wanted to switch from the require_login decorator to check if a user was in a certain group to see the function.

So the first thing I did was create a new directory in my project root called decorators and put a blank __init__.py file there. My I cated an auth.py file with the following contents.

from functools import wraps

from django.contrib.auth.models import Group
from django.http import Http404

def group_required(groups=[]):    
    def decorator(func):
        def inner_decorator(request,*args, **kwargs):
            for group in groups:
                try:
                    if Group.objects.get(name=group) in request.user.groups.all():
                        return func(request, *args, **kwargs)
                except:
                    pass

            raise Http404()

        return wraps(func)(inner_decorator)

    return decorator

So we can use it like this in our project


from decorators.auth import group_required

@group_required(["admins"])
def show_index(request):
    ....

I decided to make it a list being passed in. The main reason was to make it was to pass in multiple groups that you might want to allow to be allowed in.

My version doesn’t do much error checking.. if you want a more updated version of this function checkout my githib project I’ve started to put all my custom decorators up and online

http://github.com/mzupan/django-decorators

Reason #2 on why Django is great

The decorators that Django has are a great time saver. There are a lot of methods in your views where you only want logged in users to see. So you can use a decorator before the function to check if they are logged in or not.

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ....

So learn to use these time savers in your project.

Reason #1 on why Django is great

All the crazy helper function they have for models to display information in templates. Say I have a model that looks like this with a choice field (it will get rendered as a select input in html.

SEX_CHOICE = (
    (1, 'Male'),
    (2, 'Female'),
)
class Animal(models.Model):
    name = models.CharField(max_length=50)
    sex = models.SmallIntegerField(max_length=1, default=0, choices=SEX_CHOICE)

Now in my template I can do the following to see if an animal object is a Male or a Female

{{animal.get_sex_display}}

There is no need to create a method for that in the model. Django does it for you. Saving you time and effort.